Upgrading from 1.1 to 1.7

Copy Markdown View Source

If you pinned {:lattice_stripe, "~> 1.1"} and are moving to ~> 1.7, use this guide to answer one question first: does my existing application need a code change? It then routes you to the additive capabilities that became available in this historical interval.

Scope: this guide stops at 1.7

This guide covers the 1.1 → 1.7 leg only. The ~> 1.7 pin below is deliberate history, not a recommendation for a current installation.

Continuing to 2.x? Complete this leg, then read the 2.0.0 CHANGELOG entry and the current Getting Started and Client Configuration guides. Those guides own setup advice after this boundary.

Update your dependency

Bump the version in mix.exs and run mix deps.get:

{:lattice_stripe, "~> 1.7"}

Adopters pinned to ~> 1.1 resolve to 1.7. Retain your existing client and Finch setup for this historical leg.

Two-minute mandatory migration checklist

There are exactly three caller-visible behavior changes between 1.1 and 1.7. Check each affected-user predicate before exploring optional additions.

Breaking change: expanded fields return typed structs

Affected if: you pass expand: and pattern-match the expanded association as a raw map (for example, %{"id" => id}).

Change the match to the typed struct. If you never pass expand:, you are unaffected; unexpanded fields are still string IDs.

# Before (1.1):
{:ok, %PaymentIntent{customer: %{"id" => id}}} =
  PaymentIntent.retrieve(client, id, expand: ["customer"])

# After (1.7):
{:ok, %PaymentIntent{customer: %Customer{id: id}}} =
  PaymentIntent.retrieve(client, id, expand: ["customer"])

When expanded, associated resources now arrive as typed structs such as %Customer{}. See LatticeStripe.PaymentIntent.retrieve/3 for the representative retrieval call.

Breaking change: finite status fields return atoms

Affected if: you compare a resource's finite status field against a string (for example, pi.status == "succeeded").

Switch the comparison to the atom. Unknown or future status values still pass through as strings for forward compatibility.

# Before (1.1):
if pi.status == "succeeded" do ...

# After (1.7):
if pi.status == :succeeded do ...

Affected finite-status resources include PaymentIntent, Subscription, SubscriptionSchedule, Charge, Refund, SetupIntent, Payout, BalanceTransaction, Checkout.Session, BankAccount, Billing.Meter, and Account.Capability.

Breaking change: tolerance: 0 disables the staleness check

Affected if: you set tolerance: 0 when verifying webhook signatures in tests. It now disables the timestamp staleness check, matching the documented contract.

This is a test-only escape hatch. Never set tolerance: 0 in production; it removes replay-attack protection.

# Before (1.1): tolerance: 0 always errored
{:error, :timestamp_expired} =
  LatticeStripe.Webhook.verify_signature(payload, sig_header, secret, tolerance: 0)

# After (1.7): tolerance: 0 disables the staleness check
{:ok, _event} =
  LatticeStripe.Webhook.verify_signature(payload, sig_header, secret, tolerance: 0)

If none apply

You have no code migration for this leg. Your existing integration remains compatible. Before deployment, run your application test suite so its Stripe calls, webhook handling, and any pattern matches exercise the upgraded dependency in your own configuration.

Optional additions by job

Net-new surface — nothing here breaks

Everything below is optional. Start with the application job you need to do, use the minimum call to establish the integration, and follow the canonical guide for the complete workflow.

Payments and payment setup

NeedSurfaceMinimum callCanonical next step
Resolve a chargebackLatticeStripe.DisputeDispute.retrieve/3LatticeStripe.Dispute
Upload and submit dispute evidenceLatticeStripe.File and LatticeStripe.DisputeLatticeStripe.File.create/3 with purpose: "dispute_evidence", then Dispute.update_evidence/4 and explicit Dispute.submit_evidence/3Recipes
Create a public, expiring link to a Stripe fileLatticeStripe.FileLinkFileLink.create/3 with expires_atLatticeStripe.FileLink
Inspect a payment mandateLatticeStripe.MandateMandate.retrieve/3LatticeStripe.Mandate
Audit SetupIntent attemptsLatticeStripe.SetupAttemptSetupAttempt.list/3LatticeStripe.SetupAttempt
Search or reconcile chargesLatticeStripe.ChargeCharge.list/3 or Charge.search/3Payments

Billing and self-service

NeedSurfaceMinimum callCanonical next step
Issue a post-invoice creditLatticeStripe.CreditNoteCreditNote.create/3Credit Notes
Prepare a quote for billingLatticeStripe.QuoteQuote.create/3Quote to Billing Operator
Control customer portal cancellation behaviorLatticeStripe.BillingPortal.Configuration and SessionConfiguration.create/3, then Session.create/3 with config.idCustomer Portal

Create a portal configuration before creating a hosted session. The configuration is the policy; the session carries its id to the customer-facing portal.

{:ok, config} =
  LatticeStripe.BillingPortal.Configuration.create(client, %{
    "features" => %{"subscription_cancel" => %{"enabled" => true}}
  })

{:ok, session} =
  LatticeStripe.BillingPortal.Session.create(client, %{
    "customer" => "cus_123",
    "configuration" => config.id,
    "return_url" => "https://example.com/account"
  })

Tax

NeedSurfaceMinimum callCanonical next step
Calculate tax before chargingLatticeStripe.Tax.CalculationTax.Calculation.create/3Tax
Record or reverse a tax transactionLatticeStripe.Tax.TransactionTax.Transaction.create_from_calculation/3Tax
Read account tax settingsLatticeStripe.Tax.SettingsTax.Settings.retrieve/2Tax
Register tax obligationsLatticeStripe.Tax.RegistrationTax.Registration.create/3Tax
Attach a customer tax IDLatticeStripe.TaxIdTaxId.create/4Tax

The Tax guide owns jurisdiction, address, and reversal details; this guide only identifies the family and the first safe call.

Webhooks and operations

NeedSurfaceMinimum callCanonical next step
Verify then fetch authoritative thin-event stateLatticeStripe.EventNotificationWebhook.parse_event_notification/4, then Webhook.fetch_event/3Webhooks: Thin Events
Reconcile a bank payoutLatticeStripe.PayoutPayout.retrieve/3LatticeStripe.Payout
Inspect the ledger behind a movementLatticeStripe.BalanceTransactionBalanceTransaction.retrieve/3LatticeStripe.BalanceTransaction

For thin events, verification proves the notification came from Stripe; fetch the event before treating event payload state as authoritative.

with {:ok, notification} <-
       LatticeStripe.Webhook.parse_event_notification(payload, sig_header, secret, []),
     {:ok, event} <- LatticeStripe.Webhook.fetch_event(client, notification, []) do
  process_authoritative_event(event)
end

Testing

NeedSurfaceMinimum callCanonical next step
Create a raw Stripe test clockLatticeStripe.TestHelpers.TestClockTestHelpers.TestClock.create/3Testing
Advance billing time ergonomicallyLatticeStripe.Testing.TestClockTesting.TestClock.advance/2Testing
Build typed resource or webhook fixturesLatticeStripe.Testing.Fixturesa builder such as Testing.dispute/1Testing

Use the Testing guide for setup and isolation; these names are the 1.7 public testing surface, not a replacement for its full workflow.

Version-by-version appendix

Use this appendix for chronology after deciding required migration work and optional capability adoption.

VersionBehavior changeAdditions
1.3Expanded fields return typed structs; finite status fields return atomsDispute, CreditNote, Quote, Mandate, SetupAttempt, File, FileLink, testing fixtures
1.5tolerance: 0 disables the staleness check in testsEventNotification and parse_event_notification/4
1.6Tax.Calculation, Tax.Transaction, Tax.Settings, Tax.Registration, TaxId
1.7Charge list/search, BillingPortal.Configuration, Payout, BalanceTransaction, TestHelpers.TestClock, Testing.TestClock, Testing.Fixtures

For the authoritative release record, see the CHANGELOG.