PaymentsMigration
Migrate from Stripe to Easy Payments
Mechanical mapping from Stripe's API to Easy Labs' Payments product.
This page is the mechanical method-and-event mapping between the Stripe Node SDK and @easylabs/node. Field names mostly carry over; the larger conceptual shift is that Easy Labs collapses Stripe's PaymentIntent / Charge / SetupIntent split into a single Transfer lifecycle. See Object model differences at the bottom.
API surface mapping
| Stripe | Easy Labs | Notes |
|---|---|---|
stripe.customers.create({ email, name }) | easy.createCustomer({ first_name, last_name, email }) | First/last name are required and split into separate fields. |
stripe.customers.retrieve(id) | easy.getCustomer(id) | Identical. |
stripe.customers.update(id, …) | easy.updateCustomer(id, partial) | Identical. |
stripe.customers.list({ limit }) | easy.getCustomers({ limit, offset }) | Pagination is offset-based, not cursor-based. |
stripe.customers.listPaymentMethods(id) | easy.getCustomerPaymentInstruments(id) | Returns Payment Instruments instead of PaymentMethods. |
stripe.paymentMethods.create({ type: "card" }) | Use mountEmbeddedCheckout (@easylabs/browser) or <EmbeddedCheckout> (@easylabs/react); the iframe tokenizes and creates the Payment Instrument. | No raw card → token call on the server. |
stripe.paymentMethods.retrieve(id) | Available implicitly via easy.getCustomerPaymentInstruments(customerId).find(i => i.id === pmId) | No standalone get-by-id endpoint. |
stripe.paymentMethods.update(id, …) | easy.updatePaymentInstrument(id, partial) | Includes enabled, address, tags. |
stripe.paymentIntents.create({ amount, … }) | easy.createTransfer({ amount, currency, source }) | No two-step intent → confirm. Authorization + capture happen in one call against a Payment Instrument. |
stripe.paymentIntents.confirm(id) | n/a | Implicit; createTransfer is the equivalent of "create + confirm". |
stripe.charges.create({ … }) | easy.createTransfer({ amount, currency, source }) | Renamed; same contract. |
stripe.charges.retrieve(id) | easy.getTransfer(id) | Identical. |
stripe.charges.list({ limit }) | easy.getTransfers({ limit, offset }) | Pagination differences as above. |
stripe.refunds.create({ charge, amount }) | easy.createRefund(transferId, { refund_amount }) | Reversal is itself a Transfer with type: "REVERSAL". |
stripe.disputes.retrieve(id) | easy.getDispute(id) | Identical. |
stripe.disputes.list() | easy.getDisputes({ limit, offset }) | Pagination differences. |
stripe.disputes.update(id, { metadata }) | easy.updateDispute(id, tags) | Tags are the only mutable surface; evidence upload is dashboard-only today. |
stripe.checkout.sessions.create({ … }) | easy.createEmbeddedCheckoutSession({ … }) | success_url / cancel_url / line_items shapes carry over. Add payment_methods: ["card"] (or "crypto"). |
stripe.checkout.sessions.retrieve(id) | easy.getEmbeddedCheckoutSession(id) | Identical. |
stripe.products.create(…) / .update / .list | easy.createProduct / .updateProduct / .getProducts | Identical surface. |
stripe.prices.create(…) / .update / .list | easy.createPrice / .updatePrice / .getPrices | unit_amount is in the smallest currency unit, same as Stripe. |
stripe.subscriptions.create({ customer, items }) | easy.createSubscription({ identity_id, items, instrument_id }) | customer → identity_id. Funding source must be passed explicitly. |
stripe.subscriptions.cancel(id) | easy.cancelSubscription(id, { at_period_end: true }) | Default cancels immediately; opt into period-end via flag. |
stripe.invoices.create(…) | easy.createInvoice(…) | Field names differ; see invoice reference. |
stripe.webhookEndpoints.create(…) | easy.registerWebhookEndpoint({ url, events }) | Returns the signing secret once; persist immediately. |
stripe.webhooks.constructEvent(body, sig, sec) | EasyWebhooks.constructEvent(rawBody, sig, secret) | Identical signature. Header is x-easy-webhook-signature. |
Webhook event mapping
| Stripe event | Easy Labs event | Notes |
|---|---|---|
payment_intent.succeeded | payment.updated (state SUCCEEDED) | Easy Labs collapses intent + charge events into one payment.* stream. |
payment_intent.payment_failed | payment.updated (state FAILED) | failure_code + failure_message are populated on the Transfer. |
charge.succeeded | payment.created / payment.updated | The single Transfer lifecycle covers both. |
charge.refunded | refund.updated (state SUCCEEDED) | The reversal is its own Transfer event. |
charge.refund.updated | refund.updated | Identical semantics. |
charge.dispute.created | dispute.created | Identical. |
charge.dispute.closed | dispute.updated (terminal status) | Watch for status transitioning to WON / LOST. |
checkout.session.completed | checkout.session.completed | Identical name. |
customer.subscription.created | subscription.created | Identical. |
customer.subscription.updated | subscription.updated | Identical. |
customer.subscription.deleted | subscription.deleted | Identical. |
customer.subscription.paused | subscription.paused | Identical. |
customer.subscription.resumed | subscription.resumed | Identical. |
customer.subscription.trial_will_end | subscription.trial_will_end | Identical. |
invoice.created | invoice.created | Identical. |
invoice.finalized | invoice.finalized | Identical. |
invoice.paid | invoice.paid | Identical. |
invoice.payment_failed | invoice.payment_failed | Identical. |
customer.created | identity.created | "Identity" is the underlying processor entity for Customer. |
customer.updated | identity.updated | Identical semantics. |
Object model differences
- No PaymentIntent. Stripe separates "intent to charge" from "actual charge"; Easy Labs has one
Transferlifecycle that covers both (PENDING → SUCCEEDED | FAILED). If you usedPaymentIntent.idto correlate before/after capture, switch toTransfer.id. - Identity vs. Customer. Internally Easy Labs calls customers "identities" (you'll see
identity_idon Subscriptions,identityon Orders, andidentity.*webhook events). The SDK surface still usescustomereverywhere —identity_idis the field name on payloads. - Payment Instruments are saved by default. A successful checkout always produces a re-usable
Payment Instrument. There is no separate SetupIntent for "save card without charging" — instead, create a small auth + void or save during a real first payment. - Order is a first-class object. Stripe doesn't have one — it conflates "the transaction" with "the line items." Easy Labs separates Order (line items, totals, customer details) from Transfer (money movement), so refunds happen against the Transfer but fulfillment data lives on the Order.
- Pagination is offset-based. No
starting_after/ending_beforecursors. Passlimit+offset. - Webhook signature header. Stripe uses
Stripe-Signaturewith at=…,v1=…payload; Easy Labs usesx-easy-webhook-signature: sha256=<hex>over the raw body. The Node verifier handles this for you (EasyWebhooks.constructEvent).