-
- Código de Confirmação
+
+
+ Código do Pedido
-
+
{@order.confirmation_code}
@@ -110,7 +134,9 @@ defmodule PretexWeb.EventsLive.Confirmation do
<%!-- Total --%>
- Total Pago
+
+ {if @order.status == "confirmed", do: "Total Pago", else: "Total"}
+
{format_price(@order.total_cents)}
diff --git a/pretex/mix.exs b/pretex/mix.exs
index d207005..7ecd19a 100644
--- a/pretex/mix.exs
+++ b/pretex/mix.exs
@@ -93,7 +93,12 @@ defmodule Pretex.MixProject do
"esbuild pretex --minify",
"phx.digest"
],
- precommit: ["compile --warnings-as-errors", "deps.unlock --unused", "format", "test"]
+ precommit: [
+ "compile --warnings-as-errors",
+ "deps.unlock --unused",
+ "format",
+ "test --warnings-as-errors"
+ ]
]
end
end
diff --git a/pretex/priv/repo/migrations/20260320300000_add_oban_jobs_table.exs b/pretex/priv/repo/migrations/20260320300000_add_oban_jobs_table.exs
new file mode 100644
index 0000000..2775f81
--- /dev/null
+++ b/pretex/priv/repo/migrations/20260320300000_add_oban_jobs_table.exs
@@ -0,0 +1,11 @@
+defmodule Pretex.Repo.Migrations.AddObanJobsTable do
+ use Ecto.Migration
+
+ def up do
+ Oban.Migration.up(version: 12)
+ end
+
+ def down do
+ Oban.Migration.down(version: 1)
+ end
+end
diff --git a/pretex/priv/repo/migrations/20260320300001_add_attendee_info_to_cart_sessions.exs b/pretex/priv/repo/migrations/20260320300001_add_attendee_info_to_cart_sessions.exs
new file mode 100644
index 0000000..e1157f2
--- /dev/null
+++ b/pretex/priv/repo/migrations/20260320300001_add_attendee_info_to_cart_sessions.exs
@@ -0,0 +1,10 @@
+defmodule Pretex.Repo.Migrations.AddAttendeeInfoToCartSessions do
+ use Ecto.Migration
+
+ def change do
+ alter table(:cart_sessions) do
+ add(:attendee_name, :string)
+ add(:attendee_email, :string)
+ end
+ end
+end
diff --git a/pretex/priv/repo/migrations/20260320300002_add_transfer_note_to_payments.exs b/pretex/priv/repo/migrations/20260320300002_add_transfer_note_to_payments.exs
new file mode 100644
index 0000000..29ccedf
--- /dev/null
+++ b/pretex/priv/repo/migrations/20260320300002_add_transfer_note_to_payments.exs
@@ -0,0 +1,9 @@
+defmodule Pretex.Repo.Migrations.AddTransferNoteToPayments do
+ use Ecto.Migration
+
+ def change do
+ alter table(:payments) do
+ add(:transfer_note, :text)
+ end
+ end
+end
diff --git a/pretex/test/pretex/discount_order_integration_test.exs b/pretex/test/pretex/discount_order_integration_test.exs
index c3e4856..6855c25 100644
--- a/pretex/test/pretex/discount_order_integration_test.exs
+++ b/pretex/test/pretex/discount_order_integration_test.exs
@@ -14,7 +14,7 @@ defmodule Pretex.DiscountOrderIntegrationTest do
# Helpers
# ---------------------------------------------------------------------------
- defp cart_with_item(event, price_cents \\ 5000, quantity \\ 1) do
+ defp cart_with_item(event, price_cents, quantity \\ 1) do
item = item_fixture(event, %{price_cents: price_cents})
{:ok, cart} = Orders.create_cart(event)
{:ok, _cart_item} = Orders.add_to_cart(cart, item, quantity: quantity)
@@ -32,7 +32,7 @@ defmodule Pretex.DiscountOrderIntegrationTest do
)
end
- defp discount_rule_fixture(event, attrs \\ %{}) do
+ defp discount_rule_fixture(event, attrs) do
base = %{
name: "Regra Integração #{System.unique_integer([:positive])}",
condition_type: "min_quantity",
@@ -46,7 +46,7 @@ defmodule Pretex.DiscountOrderIntegrationTest do
rule
end
- defp voucher_fixture(event, attrs \\ %{}) do
+ defp voucher_fixture(event, attrs) do
base = %{
code: "VOUCHER#{System.unique_integer([:positive])}",
effect: "fixed_discount",
diff --git a/pretex/test/pretex/discounts_test.exs b/pretex/test/pretex/discounts_test.exs
index d3c3d16..dd6b4be 100644
--- a/pretex/test/pretex/discounts_test.exs
+++ b/pretex/test/pretex/discounts_test.exs
@@ -6,9 +6,7 @@ defmodule Pretex.DiscountsTest do
import Pretex.CatalogFixtures
alias Pretex.Discounts
- alias Pretex.Discounts.DiscountRule
alias Pretex.Discounts.OrderDiscount
- alias Pretex.Orders
alias Pretex.Repo
# ---------------------------------------------------------------------------
@@ -29,7 +27,7 @@ defmodule Pretex.DiscountsTest do
rule
end
- defp order_fixture(event, total_cents \\ 10_000) do
+ defp order_fixture(event, total_cents) do
{:ok, order} =
%Pretex.Orders.Order{}
|> Ecto.Changeset.change(%{
@@ -47,17 +45,6 @@ defmodule Pretex.DiscountsTest do
order
end
- defp cart_items_fixture(price_cents \\ 5000, quantity \\ 1) do
- [
- %{
- item_id: System.unique_integer([:positive]),
- item_variation_id: nil,
- quantity: quantity,
- unit_price_cents: price_cents
- }
- ]
- end
-
# ---------------------------------------------------------------------------
# list_discount_rules/1
# ---------------------------------------------------------------------------
diff --git a/pretex/test/pretex/gift_card_order_integration_test.exs b/pretex/test/pretex/gift_card_order_integration_test.exs
index e2868a9..3ca0b29 100644
--- a/pretex/test/pretex/gift_card_order_integration_test.exs
+++ b/pretex/test/pretex/gift_card_order_integration_test.exs
@@ -13,7 +13,7 @@ defmodule Pretex.GiftCardOrderIntegrationTest do
# Helpers
# ---------------------------------------------------------------------------
- defp cart_with_item(event, price_cents \\ 5000) do
+ defp cart_with_item(event, price_cents) do
item = item_fixture(event, %{price_cents: price_cents})
{:ok, cart} = Orders.create_cart(event)
{:ok, _cart_item} = Orders.add_to_cart(cart, item, quantity: 1)
@@ -31,7 +31,7 @@ defmodule Pretex.GiftCardOrderIntegrationTest do
)
end
- defp gift_card_fixture(org, attrs \\ %{}) do
+ defp gift_card_fixture(org, attrs) do
base = %{
code: "GC-TEST#{System.unique_integer([:positive])}",
balance_cents: 5000,
@@ -315,7 +315,7 @@ defmodule Pretex.GiftCardOrderIntegrationTest do
test "gift card applied case-insensitively" do
org = org_fixture()
event = published_event_fixture(org)
- gc = gift_card_fixture(org, %{code: "GC-CASECI01", balance_cents: 1000})
+ _gc = gift_card_fixture(org, %{code: "GC-CASECI01", balance_cents: 1000})
cart = cart_with_item(event, 5000)
subtotal = Orders.cart_total(cart)
diff --git a/pretex/test/pretex/membership_order_integration_test.exs b/pretex/test/pretex/membership_order_integration_test.exs
index c8de3f9..24cd620 100644
--- a/pretex/test/pretex/membership_order_integration_test.exs
+++ b/pretex/test/pretex/membership_order_integration_test.exs
@@ -14,7 +14,7 @@ defmodule Pretex.MembershipOrderIntegrationTest do
# Helpers
# ---------------------------------------------------------------------------
- defp cart_with_item(event, price_cents \\ 5000, quantity \\ 1) do
+ defp cart_with_item(event, price_cents, quantity \\ 1) do
item = item_fixture(event, %{price_cents: price_cents})
{:ok, cart} = Orders.create_cart(event)
{:ok, _} = Orders.add_to_cart(cart, item, quantity: quantity)
@@ -39,7 +39,10 @@ defmodule Pretex.MembershipOrderIntegrationTest do
event = published_event_fixture(org)
{:ok, mt} = Memberships.create_membership_type(org, %{name: "Gold", validity_days: 365})
- {:ok, _b} = Memberships.create_benefit(mt, %{benefit_type: "percentage_discount", value: 1500})
+
+ {:ok, _b} =
+ Memberships.create_benefit(mt, %{benefit_type: "percentage_discount", value: 1500})
+
{:ok, _m} = Memberships.grant_membership(mt, customer, org)
cart = cart_with_item(event, 20_000)
@@ -83,7 +86,10 @@ defmodule Pretex.MembershipOrderIntegrationTest do
event = published_event_fixture(org)
{:ok, mt} = Memberships.create_membership_type(org, %{name: "Gold", validity_days: 365})
- {:ok, _b} = Memberships.create_benefit(mt, %{benefit_type: "percentage_discount", value: 1500})
+
+ {:ok, _b} =
+ Memberships.create_benefit(mt, %{benefit_type: "percentage_discount", value: 1500})
+
{:ok, m} = Memberships.grant_membership(mt, customer, org)
{:ok, _} = Memberships.expire_membership(m)
@@ -105,7 +111,10 @@ defmodule Pretex.MembershipOrderIntegrationTest do
event = published_event_fixture(org2)
{:ok, mt} = Memberships.create_membership_type(org1, %{name: "Gold", validity_days: 365})
- {:ok, _b} = Memberships.create_benefit(mt, %{benefit_type: "percentage_discount", value: 1500})
+
+ {:ok, _b} =
+ Memberships.create_benefit(mt, %{benefit_type: "percentage_discount", value: 1500})
+
{:ok, _m} = Memberships.grant_membership(mt, customer, org1)
cart = cart_with_item(event, 20_000)
@@ -124,12 +133,20 @@ defmodule Pretex.MembershipOrderIntegrationTest do
customer = customer_fixture()
event = published_event_fixture(org)
- {:ok, mt_gold} = Memberships.create_membership_type(org, %{name: "Gold", validity_days: 365})
- {:ok, _b} = Memberships.create_benefit(mt_gold, %{benefit_type: "percentage_discount", value: 1500})
+ {:ok, mt_gold} =
+ Memberships.create_membership_type(org, %{name: "Gold", validity_days: 365})
+
+ {:ok, _b} =
+ Memberships.create_benefit(mt_gold, %{benefit_type: "percentage_discount", value: 1500})
+
{:ok, _m} = Memberships.grant_membership(mt_gold, customer, org)
- {:ok, mt_silver} = Memberships.create_membership_type(org, %{name: "Silver", validity_days: 365})
- {:ok, _b} = Memberships.create_benefit(mt_silver, %{benefit_type: "percentage_discount", value: 1000})
+ {:ok, mt_silver} =
+ Memberships.create_membership_type(org, %{name: "Silver", validity_days: 365})
+
+ {:ok, _b} =
+ Memberships.create_benefit(mt_silver, %{benefit_type: "percentage_discount", value: 1000})
+
{:ok, _m} = Memberships.grant_membership(mt_silver, customer, org)
cart = cart_with_item(event, 20_000)
@@ -168,7 +185,10 @@ defmodule Pretex.MembershipOrderIntegrationTest do
# Membership: 10% off
{:ok, mt} = Memberships.create_membership_type(org, %{name: "Gold", validity_days: 365})
- {:ok, _b} = Memberships.create_benefit(mt, %{benefit_type: "percentage_discount", value: 1000})
+
+ {:ok, _b} =
+ Memberships.create_benefit(mt, %{benefit_type: "percentage_discount", value: 1000})
+
{:ok, _m} = Memberships.grant_membership(mt, customer, org)
# Auto-discount: fixed R$5
@@ -218,7 +238,9 @@ defmodule Pretex.MembershipOrderIntegrationTest do
event = published_event_fixture(org)
{:ok, mt} = Memberships.create_membership_type(org, %{name: "Gold", validity_days: 365})
- {:ok, _b} = Memberships.create_benefit(mt, %{benefit_type: "percentage_discount", value: 1500})
+
+ {:ok, _b} =
+ Memberships.create_benefit(mt, %{benefit_type: "percentage_discount", value: 1500})
# Create a membership item in the catalog
item =
diff --git a/pretex/test/pretex/memberships_test.exs b/pretex/test/pretex/memberships_test.exs
index 329d436..5671013 100644
--- a/pretex/test/pretex/memberships_test.exs
+++ b/pretex/test/pretex/memberships_test.exs
@@ -113,7 +113,10 @@ defmodule Pretex.MembershipsTest do
{:ok, mt} = Memberships.create_membership_type(org, %{name: "Gold", validity_days: 365})
assert {:error, changeset} =
- Memberships.create_benefit(mt, %{benefit_type: "percentage_discount", value: 10_001})
+ Memberships.create_benefit(mt, %{
+ benefit_type: "percentage_discount",
+ value: 10_001
+ })
assert errors_on(changeset).value != []
end
@@ -170,7 +173,10 @@ defmodule Pretex.MembershipsTest do
org = org_fixture()
customer = customer_fixture()
{:ok, mt} = Memberships.create_membership_type(org, %{name: "Gold", validity_days: 365})
- {:ok, _b} = Memberships.create_benefit(mt, %{benefit_type: "percentage_discount", value: 1500})
+
+ {:ok, _b} =
+ Memberships.create_benefit(mt, %{benefit_type: "percentage_discount", value: 1500})
+
{:ok, _m} = Memberships.grant_membership(mt, customer, org)
memberships = Memberships.active_memberships_for_checkout(customer, org)
diff --git a/pretex/test/pretex/voucher_order_integration_test.exs b/pretex/test/pretex/voucher_order_integration_test.exs
index c41b6c6..9dc8388 100644
--- a/pretex/test/pretex/voucher_order_integration_test.exs
+++ b/pretex/test/pretex/voucher_order_integration_test.exs
@@ -13,7 +13,7 @@ defmodule Pretex.VoucherOrderIntegrationTest do
# Helpers
# ---------------------------------------------------------------------------
- defp cart_with_item(event, price_cents \\ 5000) do
+ defp cart_with_item(event, price_cents) do
item = item_fixture(event, %{price_cents: price_cents})
{:ok, cart} = Orders.create_cart(event)
{:ok, _cart_item} = Orders.add_to_cart(cart, item, quantity: 1)
@@ -31,7 +31,7 @@ defmodule Pretex.VoucherOrderIntegrationTest do
)
end
- defp voucher_fixture(event, attrs \\ %{}) do
+ defp voucher_fixture(event, attrs) do
base = %{
code: "TESTCODE#{System.unique_integer([:positive])}",
effect: "fixed_discount",
diff --git a/pretex/test/pretex_web/controllers/page_controller_test.exs b/pretex/test/pretex_web/controllers/page_controller_test.exs
index c2f1ad0..7a13eb9 100644
--- a/pretex/test/pretex_web/controllers/page_controller_test.exs
+++ b/pretex/test/pretex_web/controllers/page_controller_test.exs
@@ -1,27 +1,91 @@
defmodule PretexWeb.PageControllerTest do
use PretexWeb.ConnCase, async: true
- test "GET / renders the landing page with key sections", %{conn: conn} do
- conn = get(conn, ~p"/")
- html = html_response(conn, 200)
+ describe "GET / - unauthenticated" do
+ test "renders the landing page with key sections", %{conn: conn} do
+ conn = get(conn, ~p"/")
+ html = html_response(conn, 200)
- # Hero section
- assert html =~ "Pretex"
- assert html =~ "plataforma"
+ assert html =~ "Pretex"
+ assert html =~ "plataforma"
+ end
- # Features section
- assert html =~ "Gestão de Eventos"
- assert html =~ "Venda de Ingressos"
+ test "renders features section", %{conn: conn} do
+ conn = get(conn, ~p"/")
+ html = html_response(conn, 200)
- # CTA
- assert html =~ "Começar Agora"
+ assert html =~ "Gestão de Eventos"
+ assert html =~ "Venda de Ingressos"
+ end
+
+ test "renders call-to-action links", %{conn: conn} do
+ conn = get(conn, ~p"/")
+ html = html_response(conn, 200)
+
+ assert html =~ "Começar Agora"
+ end
+
+ test "navbar shows login and register links when not authenticated", %{conn: conn} do
+ conn = get(conn, ~p"/")
+ html = html_response(conn, 200)
+
+ assert html =~ ~s(href="/customers/log-in")
+ assert html =~ ~s(href="/customers/register")
+ end
+
+ test "navbar does not show logout or orders links when not authenticated", %{conn: conn} do
+ conn = get(conn, ~p"/")
+ html = html_response(conn, 200)
+
+ refute html =~ "Meus Pedidos"
+ refute html =~ "Sair"
+ end
+
+ test "contains navigation links to events and register", %{conn: conn} do
+ conn = get(conn, ~p"/")
+ html = html_response(conn, 200)
+
+ assert html =~ ~s(href="/events")
+ assert html =~ ~s(href="/customers/register")
+ end
end
- test "GET / contains navigation links", %{conn: conn} do
- conn = get(conn, ~p"/")
- html = html_response(conn, 200)
+ describe "GET / - authenticated" do
+ setup :register_and_log_in_customer
+
+ test "renders the landing page successfully", %{conn: conn} do
+ conn = get(conn, ~p"/")
+ assert html_response(conn, 200) =~ "Pretex"
+ end
+
+ test "navbar shows customer email when authenticated", %{conn: conn, customer: customer} do
+ conn = get(conn, ~p"/")
+ html = html_response(conn, 200)
+
+ assert html =~ customer.email
+ end
+
+ test "navbar shows orders and logout links when authenticated", %{conn: conn} do
+ conn = get(conn, ~p"/")
+ html = html_response(conn, 200)
+
+ assert html =~ "Meus Pedidos"
+ assert html =~ "Sair"
+ end
+
+ test "navbar does not show login or register links when authenticated", %{conn: conn} do
+ conn = get(conn, ~p"/")
+ html = html_response(conn, 200)
+
+ refute html =~ "Entrar"
+ refute html =~ "Criar Conta"
+ end
+
+ test "navbar orders link points to account orders page", %{conn: conn} do
+ conn = get(conn, ~p"/")
+ html = html_response(conn, 200)
- assert html =~ ~s(href="/events")
- assert html =~ ~s(href="/customers/register")
+ assert html =~ ~s(href="/account/orders")
+ end
end
end
diff --git a/pretex/test/pretex_web/live/admin/discount_live_test.exs b/pretex/test/pretex_web/live/admin/discount_live_test.exs
index 46cf4ad..9b4809b 100644
--- a/pretex/test/pretex_web/live/admin/discount_live_test.exs
+++ b/pretex/test/pretex_web/live/admin/discount_live_test.exs
@@ -32,7 +32,7 @@ defmodule PretexWeb.Admin.DiscountLiveTest do
event
end
- defp discount_rule_fixture(event, attrs \\ %{}) do
+ defp discount_rule_fixture(event, attrs) do
base = %{
name: "Regra Teste #{System.unique_integer([:positive])}",
condition_type: "min_quantity",
diff --git a/pretex/test/pretex_web/live/admin/gift_card_live_test.exs b/pretex/test/pretex_web/live/admin/gift_card_live_test.exs
index de14f51..4649e88 100644
--- a/pretex/test/pretex_web/live/admin/gift_card_live_test.exs
+++ b/pretex/test/pretex_web/live/admin/gift_card_live_test.exs
@@ -19,7 +19,7 @@ defmodule PretexWeb.Admin.GiftCardLiveTest do
org
end
- defp gift_card_fixture(org, attrs \\ %{}) do
+ defp gift_card_fixture(org, attrs) do
base = %{
code: "GC-TEST#{System.unique_integer([:positive])}",
balance_cents: 5000,
diff --git a/pretex/test/pretex_web/live/admin/payment_live_test.exs b/pretex/test/pretex_web/live/admin/payment_live_test.exs
index e2bd245..3580db9 100644
--- a/pretex/test/pretex_web/live/admin/payment_live_test.exs
+++ b/pretex/test/pretex_web/live/admin/payment_live_test.exs
@@ -88,7 +88,7 @@ defmodule PretexWeb.Admin.PaymentLiveTest do
test "exibe seleção de tipos de provedores", %{conn: conn} do
org = org_fixture()
- {:ok, view, _html} = live(conn, ~p"/admin/organizations/#{org}/payments")
+ {:ok, _view, _html} = live(conn, ~p"/admin/organizations/#{org}/payments")
# Navigate to provider selection page
{:ok, _view, html} = live(conn, ~p"/admin/organizations/#{org}/payments/new")
diff --git a/pretex/test/pretex_web/live/checkout_gift_card_test.exs b/pretex/test/pretex_web/live/checkout_gift_card_test.exs
index cbc0043..8565b3b 100644
--- a/pretex/test/pretex_web/live/checkout_gift_card_test.exs
+++ b/pretex/test/pretex_web/live/checkout_gift_card_test.exs
@@ -57,7 +57,7 @@ defmodule PretexWeb.CheckoutGiftCardTest do
Orders.get_cart_by_token(cart.session_token)
end
- defp gift_card_fixture(org, attrs \\ %{}) do
+ defp gift_card_fixture(org, attrs) do
base = %{
code: "GC-TEST#{System.unique_integer([:positive])}",
balance_cents: 5000,
@@ -183,7 +183,7 @@ defmodule PretexWeb.CheckoutGiftCardTest do
event = event_fixture(org)
item = item_fixture(event, 5000)
cart = cart_fixture(event, item)
- gc = gift_card_fixture(org, %{code: "GC-CITEST1", balance_cents: 1000})
+ _gc = gift_card_fixture(org, %{code: "GC-CITEST1", balance_cents: 1000})
{:ok, view, _html} = navigate_to_summary(conn, event, cart)
diff --git a/pretex/test/pretex_web/live/checkout_voucher_test.exs b/pretex/test/pretex_web/live/checkout_voucher_test.exs
index 5bb954a..1f45204 100644
--- a/pretex/test/pretex_web/live/checkout_voucher_test.exs
+++ b/pretex/test/pretex_web/live/checkout_voucher_test.exs
@@ -58,7 +58,7 @@ defmodule PretexWeb.CheckoutVoucherTest do
Orders.get_cart_by_token(cart.session_token)
end
- defp voucher_fixture(event, attrs \\ %{}) do
+ defp voucher_fixture(event, attrs) do
base = %{
code: "VOUCHER#{System.unique_integer([:positive])}",
effect: "fixed_discount",
diff --git a/pretex/test/pretex_web/live/events_live/checkout_e2e_test.exs b/pretex/test/pretex_web/live/events_live/checkout_e2e_test.exs
new file mode 100644
index 0000000..ee851d7
--- /dev/null
+++ b/pretex/test/pretex_web/live/events_live/checkout_e2e_test.exs
@@ -0,0 +1,561 @@
+defmodule PretexWeb.EventsLive.CheckoutE2ETest do
+ use PretexWeb.ConnCase, async: true
+
+ import Phoenix.LiveViewTest
+ import Pretex.OrganizationsFixtures
+ import Pretex.EventsFixtures
+ import Pretex.CatalogFixtures
+
+ alias Pretex.Orders
+ alias Pretex.Payments
+ alias Pretex.Repo
+
+ # ---------------------------------------------------------------------------
+ # Shared helpers
+ # ---------------------------------------------------------------------------
+
+ defp manual_provider_fixture(org) do
+ {:ok, provider} =
+ Payments.create_provider(%{
+ organization_id: org.id,
+ type: "manual",
+ name: "Transferência Bancária E2E #{System.unique_integer([:positive])}",
+ credentials: %{"bank_info" => "Banco do Brasil Ag 0001 CC 12345-6"},
+ is_active: true
+ })
+
+ {:ok, provider} = Payments.validate_provider(provider)
+ provider
+ end
+
+ # Extracts a named query-string parameter from a URL path string.
+ defp query_param(path, key) do
+ path
+ |> URI.parse()
+ |> Map.get(:query, "")
+ |> URI.decode_query()
+ |> Map.fetch!(key)
+ end
+
+ # ---------------------------------------------------------------------------
+ # End-to-end: events list → event page → add to cart → checkout → confirmation
+ # ---------------------------------------------------------------------------
+
+ describe "full purchase flow" do
+ test "user browses events, adds a ticket to cart and completes purchase", %{conn: conn} do
+ org = org_fixture()
+ event = published_event_fixture(org)
+ item = item_fixture(event, %{name: "Ingresso Geral", price_cents: 5000})
+ provider = manual_provider_fixture(org)
+
+ # 1. Events list
+ {:ok, _view, html} = live(conn, ~p"/events")
+ assert html =~ event.name
+
+ # 2. Event detail page
+ {:ok, view, html} = live(conn, ~p"/events/#{event.slug}")
+ assert html =~ event.name
+ assert html =~ item.name
+
+ # 3. Add ticket to cart — the page push_patches with the cart token
+ view |> element("#add-#{item.id}") |> render_click()
+ patch_path = assert_patch(view)
+ assert patch_path =~ "cart_token"
+
+ cart_token = query_param(patch_path, "cart_token")
+
+ # 4. Checkout — info step
+ {:ok, view, html} =
+ live(conn, ~p"/events/#{event.slug}/checkout?cart_token=#{cart_token}")
+
+ assert html =~ "Suas Informações"
+
+ view
+ |> form("#checkout-info-form", %{
+ checkout: %{name: "João Silva", email: "joao@test.com"}
+ })
+ |> render_submit()
+
+ assert_patch(view)
+
+ # 5. Summary step
+ html = render(view)
+ assert html =~ "Resumo do Pedido"
+ assert html =~ item.name
+
+ # 6. Select payment method
+ view
+ |> element("#pay-bank_transfer")
+ |> render_click(%{"method" => "bank_transfer", "provider-id" => to_string(provider.id)})
+
+ # 7. Place order
+ view |> element("#place-order-btn") |> render_click()
+
+ payment_path = assert_patch(view)
+ assert payment_path =~ "/checkout/payment"
+
+ order_code = query_param(payment_path, "order_code")
+ {:ok, order} = Orders.get_order_by_confirmation_code(order_code)
+ payment = Payments.get_payment_for_order(order)
+
+ # 8. Confirm payment (simulating admin/webhook)
+ {:ok, _} = Payments.confirm_payment(payment)
+
+ assert_redirect(view, ~p"/events/#{event.slug}/orders/#{order_code}")
+
+ # 9. Order confirmation page
+ {:ok, _view, html} = live(conn, ~p"/events/#{event.slug}/orders/#{order_code}")
+ assert html =~ "Pedido Confirmado!"
+ assert html =~ "João Silva"
+ assert html =~ "R$ 50,00"
+ end
+
+ # Regression test for the "cart expired" bug:
+ # - extend_cart_expiry updated the DB row but the stale CartSession struct
+ # (with the old expires_at) was still stored in socket.assigns.cart.
+ # - create_order_from_cart received the stale struct and validate_cart_not_expired
+ # always returned {:error, :cart_expired}.
+ test "an already-expired cart is recoverable — visiting checkout extends the TTL", %{
+ conn: conn
+ } do
+ org = org_fixture()
+ event = published_event_fixture(org)
+ item = item_fixture(event, %{name: "Ingresso VIP", price_cents: 15000})
+ provider = manual_provider_fixture(org)
+
+ {:ok, raw_cart} = Orders.create_cart(event)
+ Orders.add_to_cart(raw_cart, item)
+ cart = Orders.get_cart_by_token(raw_cart.session_token)
+
+ # Backdate the expiry so the cart looks already expired
+ expired_at =
+ DateTime.utc_now() |> DateTime.add(-120, :second) |> DateTime.truncate(:second)
+
+ Repo.update!(Ecto.Changeset.change(cart, expires_at: expired_at))
+
+ # Opening the checkout page must extend the TTL and NOT redirect away
+ {:ok, view, html} =
+ live(conn, ~p"/events/#{event.slug}/checkout?cart_token=#{cart.session_token}")
+
+ refute html =~ "expirou"
+ assert html =~ "Suas Informações"
+
+ # Fill in info — the form must accept the submission
+ view
+ |> form("#checkout-info-form", %{
+ checkout: %{name: "Maria Souza", email: "maria@test.com"}
+ })
+ |> render_submit()
+
+ assert_patch(view)
+ assert render(view) =~ "Resumo do Pedido"
+
+ # Select payment method and place the order — must NOT raise cart_expired
+ view
+ |> element("#pay-bank_transfer")
+ |> render_click(%{"method" => "bank_transfer", "provider-id" => to_string(provider.id)})
+
+ view |> element("#place-order-btn") |> render_click()
+
+ # Reaching the payment step proves no cart_expired error was raised
+ payment_path = assert_patch(view)
+ assert payment_path =~ "/checkout/payment"
+
+ order_code = query_param(payment_path, "order_code")
+ {:ok, order} = Orders.get_order_by_confirmation_code(order_code)
+ payment = Payments.get_payment_for_order(order)
+ {:ok, _} = Payments.confirm_payment(payment)
+
+ assert_redirect(view, ~p"/events/#{event.slug}/orders/#{order_code}")
+
+ {:ok, _view, html} = live(conn, ~p"/events/#{event.slug}/orders/#{order_code}")
+ assert html =~ "Pedido Confirmado!"
+ assert html =~ "Maria Souza"
+ assert html =~ "R$ 150,00"
+ end
+ end
+
+ # ---------------------------------------------------------------------------
+ # Bank transfer note submission
+ # ---------------------------------------------------------------------------
+
+ describe "bank transfer note" do
+ test "payment step shows the transfer note form for bank_transfer", %{conn: conn} do
+ org = org_fixture()
+ event = published_event_fixture(org)
+ item = item_fixture(event, %{name: "Ingresso TED", price_cents: 8000})
+ provider = manual_provider_fixture(org)
+
+ {:ok, raw_cart} = Orders.create_cart(event)
+ Orders.add_to_cart(raw_cart, item)
+ cart = Orders.get_cart_by_token(raw_cart.session_token)
+
+ {:ok, order} =
+ Orders.create_order_from_cart(cart, %{
+ name: "Ana Lima",
+ email: "ana@example.com",
+ payment_method: "bank_transfer",
+ payment_provider_id: provider.id
+ })
+
+ {:ok, _payment} = Payments.create_payment(order, provider, "bank_transfer")
+
+ {:ok, view, html} =
+ live(
+ conn,
+ ~p"/events/#{event.slug}/checkout/payment?cart_token=#{cart.session_token}&order_code=#{order.confirmation_code}"
+ )
+
+ assert html =~ "Envie o comprovante da transferência"
+ assert has_element?(view, "#transfer-note-input")
+ end
+
+ test "submitting a transfer note saves it and shows confirmation", %{conn: conn} do
+ org = org_fixture()
+ event = published_event_fixture(org)
+ item = item_fixture(event, %{name: "Ingresso PIX", price_cents: 3000})
+ provider = manual_provider_fixture(org)
+
+ {:ok, raw_cart} = Orders.create_cart(event)
+ Orders.add_to_cart(raw_cart, item)
+ cart = Orders.get_cart_by_token(raw_cart.session_token)
+
+ {:ok, order} =
+ Orders.create_order_from_cart(cart, %{
+ name: "Carlos Melo",
+ email: "carlos@example.com",
+ payment_method: "bank_transfer",
+ payment_provider_id: provider.id
+ })
+
+ {:ok, _payment} = Payments.create_payment(order, provider, "bank_transfer")
+
+ {:ok, view, _html} =
+ live(
+ conn,
+ ~p"/events/#{event.slug}/checkout/payment?cart_token=#{cart.session_token}&order_code=#{order.confirmation_code}"
+ )
+
+ view
+ |> element("form[phx-submit='submit_transfer_note']")
+ |> render_submit(%{"note" => "TED · ID 123456 · R$ 30,00"})
+
+ html = render(view)
+ assert html =~ "Comprovante enviado!"
+
+ payment = Payments.get_payment_for_order(order)
+ assert payment.transfer_note == "TED · ID 123456 · R$ 30,00"
+ end
+
+ test "submitting an empty note shows an error and does not persist", %{conn: conn} do
+ org = org_fixture()
+ event = published_event_fixture(org)
+ item = item_fixture(event, %{name: "Ingresso DOC", price_cents: 4500})
+ provider = manual_provider_fixture(org)
+
+ {:ok, raw_cart} = Orders.create_cart(event)
+ Orders.add_to_cart(raw_cart, item)
+ cart = Orders.get_cart_by_token(raw_cart.session_token)
+
+ {:ok, order} =
+ Orders.create_order_from_cart(cart, %{
+ name: "Paula Dias",
+ email: "paula@example.com",
+ payment_method: "bank_transfer",
+ payment_provider_id: provider.id
+ })
+
+ {:ok, _payment} = Payments.create_payment(order, provider, "bank_transfer")
+
+ {:ok, view, _html} =
+ live(
+ conn,
+ ~p"/events/#{event.slug}/checkout/payment?cart_token=#{cart.session_token}&order_code=#{order.confirmation_code}"
+ )
+
+ view
+ |> element("form[phx-submit='submit_transfer_note']")
+ |> render_submit(%{"note" => " "})
+
+ html = render(view)
+ # Form stays visible and note was not persisted
+ refute html =~ "Comprovante enviado!"
+
+ payment = Payments.get_payment_for_order(order)
+ assert is_nil(payment.transfer_note)
+ end
+ end
+
+ # ---------------------------------------------------------------------------
+ # customer_id linking — orders appear in /account/orders
+ # ---------------------------------------------------------------------------
+
+ describe "customer_id on orders" do
+ test "order created by a logged-in customer is linked to their account", %{conn: conn} do
+ org = org_fixture()
+ event = published_event_fixture(org)
+ item = item_fixture(event, %{price_cents: 5000})
+ provider = manual_provider_fixture(org)
+
+ # Log in as a customer
+ %{conn: authed_conn, customer: customer} = register_and_log_in_customer(%{conn: conn})
+
+ {:ok, raw_cart} = Orders.create_cart(event)
+ Orders.add_to_cart(raw_cart, item)
+ cart = Orders.get_cart_by_token(raw_cart.session_token)
+
+ {:ok, view, _html} =
+ live(authed_conn, ~p"/events/#{event.slug}/checkout?cart_token=#{cart.session_token}")
+
+ view
+ |> form("#checkout-info-form", %{
+ checkout: %{name: "Sofia Rocha", email: "sofia@example.com"}
+ })
+ |> render_submit()
+
+ assert_patch(view)
+
+ view
+ |> element("#pay-bank_transfer")
+ |> render_click(%{"method" => "bank_transfer", "provider-id" => to_string(provider.id)})
+
+ view |> element("#place-order-btn") |> render_click()
+ payment_path = assert_patch(view)
+ order_code = query_param(payment_path, "order_code")
+
+ {:ok, order} = Orders.get_order_by_confirmation_code(order_code)
+ assert order.customer_id == customer.id
+
+ # The order must appear in /account/orders
+ {:ok, _view, html} = live(authed_conn, ~p"/account/orders")
+ assert html =~ order_code
+ end
+
+ test "order created by a guest has no customer_id", %{conn: conn} do
+ org = org_fixture()
+ event = published_event_fixture(org)
+ item = item_fixture(event, %{price_cents: 2000})
+ provider = manual_provider_fixture(org)
+
+ {:ok, raw_cart} = Orders.create_cart(event)
+ Orders.add_to_cart(raw_cart, item)
+ cart = Orders.get_cart_by_token(raw_cart.session_token)
+
+ # Unauthenticated connection — no current_scope customer
+ {:ok, view, _html} =
+ live(conn, ~p"/events/#{event.slug}/checkout?cart_token=#{cart.session_token}")
+
+ view
+ |> form("#checkout-info-form", %{
+ checkout: %{name: "Visitante Guest", email: "guest@example.com"}
+ })
+ |> render_submit()
+
+ assert_patch(view)
+
+ view
+ |> element("#pay-bank_transfer")
+ |> render_click(%{"method" => "bank_transfer", "provider-id" => to_string(provider.id)})
+
+ view |> element("#place-order-btn") |> render_click()
+ payment_path = assert_patch(view)
+ order_code = query_param(payment_path, "order_code")
+
+ {:ok, order} = Orders.get_order_by_confirmation_code(order_code)
+ assert is_nil(order.customer_id)
+ end
+ end
+
+ # ---------------------------------------------------------------------------
+ # Admin payment confirmation
+ # ---------------------------------------------------------------------------
+
+ describe "admin confirm payment" do
+ setup :register_and_log_in_user
+
+ test "admin sees the payment card with transfer note on the order show page", %{conn: conn} do
+ org = org_fixture()
+ event = published_event_fixture(org)
+ item = item_fixture(event, %{price_cents: 7500})
+ provider = manual_provider_fixture(org)
+
+ {:ok, raw_cart} = Orders.create_cart(event)
+ Orders.add_to_cart(raw_cart, item)
+ cart = Orders.get_cart_by_token(raw_cart.session_token)
+
+ {:ok, order} =
+ Orders.create_order_from_cart(cart, %{
+ name: "Luisa Ferreira",
+ email: "luisa@example.com",
+ payment_method: "bank_transfer",
+ payment_provider_id: provider.id
+ })
+
+ {:ok, payment} = Payments.create_payment(order, provider, "bank_transfer")
+ {:ok, _} = Payments.update_payment_transfer_note(payment, "TED confirmada · ID 998877")
+
+ {:ok, _view, html} =
+ live(conn, ~p"/admin/organizations/#{org}/events/#{event}/orders/#{order.id}")
+
+ assert html =~ "Comprovante enviado pelo cliente"
+ assert html =~ "TED confirmada · ID 998877"
+ assert html =~ "Confirmar Pagamento"
+ end
+
+ test "admin sees placeholder when no transfer note was submitted", %{conn: conn} do
+ org = org_fixture()
+ event = published_event_fixture(org)
+ item = item_fixture(event, %{price_cents: 4000})
+ provider = manual_provider_fixture(org)
+
+ {:ok, raw_cart} = Orders.create_cart(event)
+ Orders.add_to_cart(raw_cart, item)
+ cart = Orders.get_cart_by_token(raw_cart.session_token)
+
+ {:ok, order} =
+ Orders.create_order_from_cart(cart, %{
+ name: "Ricardo Nunes",
+ email: "ricardo@example.com",
+ payment_method: "bank_transfer",
+ payment_provider_id: provider.id
+ })
+
+ {:ok, _payment} = Payments.create_payment(order, provider, "bank_transfer")
+
+ {:ok, _view, html} =
+ live(conn, ~p"/admin/organizations/#{org}/events/#{event}/orders/#{order.id}")
+
+ assert html =~ "Nenhum comprovante enviado ainda"
+ assert html =~ "Confirmar Pagamento"
+ end
+
+ test "admin clicking confirm_payment confirms the order and updates the UI", %{conn: conn} do
+ org = org_fixture()
+ event = published_event_fixture(org)
+ item = item_fixture(event, %{price_cents: 6000})
+ provider = manual_provider_fixture(org)
+
+ {:ok, raw_cart} = Orders.create_cart(event)
+ Orders.add_to_cart(raw_cart, item)
+ cart = Orders.get_cart_by_token(raw_cart.session_token)
+
+ {:ok, order} =
+ Orders.create_order_from_cart(cart, %{
+ name: "Beatriz Teles",
+ email: "beatriz@example.com",
+ payment_method: "bank_transfer",
+ payment_provider_id: provider.id
+ })
+
+ {:ok, _payment} = Payments.create_payment(order, provider, "bank_transfer")
+
+ {:ok, view, html} =
+ live(conn, ~p"/admin/organizations/#{org}/events/#{event}/orders/#{order.id}")
+
+ assert html =~ "Pendente"
+ assert html =~ "Confirmar Pagamento"
+
+ view |> element("#confirm-payment-card") |> render_click()
+
+ html = render(view)
+ assert html =~ "Confirmado"
+ assert html =~ "Pagamento confirmado com sucesso"
+ refute has_element?(view, "#confirm-payment-card")
+
+ # The order must be confirmed in the database
+ confirmed_order = Orders.get_order_with_details!(order.id)
+ assert confirmed_order.status == "confirmed"
+ end
+
+ test "admin cannot see confirm button for an already confirmed order", %{conn: conn} do
+ org = org_fixture()
+ event = published_event_fixture(org)
+ item = item_fixture(event, %{price_cents: 5000})
+ provider = manual_provider_fixture(org)
+
+ {:ok, raw_cart} = Orders.create_cart(event)
+ Orders.add_to_cart(raw_cart, item)
+ cart = Orders.get_cart_by_token(raw_cart.session_token)
+
+ {:ok, order} =
+ Orders.create_order_from_cart(cart, %{
+ name: "Fernanda Leal",
+ email: "fernanda@example.com",
+ payment_method: "bank_transfer",
+ payment_provider_id: provider.id
+ })
+
+ {:ok, payment} = Payments.create_payment(order, provider, "bank_transfer")
+ {:ok, _} = Payments.confirm_payment(payment)
+
+ {:ok, _view, html} =
+ live(conn, ~p"/admin/organizations/#{org}/events/#{event}/orders/#{order.id}")
+
+ assert html =~ "Confirmado"
+ refute html =~ "Confirmar Pagamento"
+ end
+ end
+
+ # ---------------------------------------------------------------------------
+ # Confirmation page status display
+ # ---------------------------------------------------------------------------
+
+ describe "confirmation page status display" do
+ test "pending order shows 'Aguardando Pagamento' hero", %{conn: conn} do
+ org = org_fixture()
+ event = published_event_fixture(org)
+ item = item_fixture(event, %{price_cents: 2000})
+ provider = manual_provider_fixture(org)
+
+ {:ok, raw_cart} = Orders.create_cart(event)
+ Orders.add_to_cart(raw_cart, item)
+ cart = Orders.get_cart_by_token(raw_cart.session_token)
+
+ {:ok, order} =
+ Orders.create_order_from_cart(cart, %{
+ name: "Guest Pendente",
+ email: "pending@example.com",
+ payment_method: "bank_transfer",
+ payment_provider_id: provider.id
+ })
+
+ {:ok, _payment} = Payments.create_payment(order, provider, "bank_transfer")
+
+ {:ok, _view, html} =
+ live(conn, ~p"/events/#{event.slug}/orders/#{order.confirmation_code}")
+
+ assert html =~ "Aguardando Pagamento"
+ refute html =~ "Pedido Confirmado!"
+ assert html =~ order.confirmation_code
+ end
+
+ test "confirmed order shows 'Pedido Confirmado!' hero", %{conn: conn} do
+ org = org_fixture()
+ event = published_event_fixture(org)
+ item = item_fixture(event, %{price_cents: 3000})
+ provider = manual_provider_fixture(org)
+
+ {:ok, raw_cart} = Orders.create_cart(event)
+ Orders.add_to_cart(raw_cart, item)
+ cart = Orders.get_cart_by_token(raw_cart.session_token)
+
+ {:ok, order} =
+ Orders.create_order_from_cart(cart, %{
+ name: "Guest Confirmado",
+ email: "confirmed@example.com",
+ payment_method: "bank_transfer",
+ payment_provider_id: provider.id
+ })
+
+ {:ok, payment} = Payments.create_payment(order, provider, "bank_transfer")
+ {:ok, _} = Payments.confirm_payment(payment)
+
+ {:ok, _view, html} =
+ live(conn, ~p"/events/#{event.slug}/orders/#{order.confirmation_code}")
+
+ assert html =~ "Pedido Confirmado!"
+ refute html =~ "Aguardando Pagamento"
+ assert html =~ order.confirmation_code
+ end
+ end
+end
diff --git a/pretex/test/pretex_web/live/events_live/events_live_test.exs b/pretex/test/pretex_web/live/events_live/events_live_test.exs
index 5ee64ed..555adad 100644
--- a/pretex/test/pretex_web/live/events_live/events_live_test.exs
+++ b/pretex/test/pretex_web/live/events_live/events_live_test.exs
@@ -328,6 +328,34 @@ defmodule PretexWeb.EventsLiveTest do
assert html =~ "Resumo do Pedido"
assert html =~ "Pagamento"
end
+
+ test "attendee name and email are restored after a page refresh", %{conn: conn} do
+ org = org_fixture()
+ event = published_event_fixture(org)
+ item = item_fixture(event)
+ cart = cart_with_item_fixture(event, item)
+
+ # Fill in the info form and submit — this persists name/email to the cart
+ {:ok, view, _html} =
+ live(conn, ~p"/events/#{event.slug}/checkout?cart_token=#{cart.session_token}")
+
+ view
+ |> form("#checkout-info-form", %{
+ checkout: %{name: "Maria Silva", email: "maria@example.com"}
+ })
+ |> render_submit()
+
+ # Simulate a full page refresh by mounting a brand-new LiveView process
+ # for the same URL (summary step). The attendee info must come from the DB.
+ {:ok, _view2, html} =
+ live(
+ conn,
+ ~p"/events/#{event.slug}/checkout/summary?cart_token=#{cart.session_token}"
+ )
+
+ assert html =~ "Maria Silva"
+ assert html =~ "maria@example.com"
+ end
end
describe "Checkout - summary step" do
@@ -392,7 +420,7 @@ defmodule PretexWeb.EventsLiveTest do
assert has_element?(view, "#pay-pix")
end
- test "place_order navigates away after checkout", %{conn: conn} do
+ test "place_order navigates to payment step after checkout", %{conn: conn} do
org = org_fixture()
event = published_event_fixture(org)
item = item_fixture(event, %{price_cents: 1000})
@@ -408,25 +436,62 @@ defmodule PretexWeb.EventsLiveTest do
|> form("#checkout-info-form", %{checkout: %{name: "Alice Smith", email: "alice@test.com"}})
|> render_submit()
+ # Drain the push_patch to the summary step before proceeding
+ assert_patch(view)
+
# Step 2: select a payment method available from the manual provider
view
|> element("#pay-bank_transfer")
|> render_click(%{"method" => "bank_transfer", "provider-id" => to_string(provider.id)})
- # Step 3: place order — the LiveView creates the order and navigates
+ # Step 3: place order — the LiveView creates the order and patch-navigates
+ # to the payment step (async flow for manual/bank_transfer provider)
+ view
+ |> element("#place-order-btn")
+ |> render_click()
+
+ path = assert_patch(view)
+ assert path =~ "/checkout/payment"
+ end
+
+ test "an expired cart is recoverable — page load extends the TTL", %{conn: conn} do
+ org = org_fixture()
+ event = published_event_fixture(org)
+ item = item_fixture(event, %{price_cents: 1000})
+ provider = payment_provider_fixture(org, "manual")
+ cart = cart_with_item_fixture(event, item)
+
+ # Backdate the cart so it looks already expired before the page opens
+ expired_at =
+ DateTime.utc_now() |> DateTime.add(-60, :second) |> DateTime.truncate(:second)
+
+ Pretex.Repo.update!(Ecto.Changeset.change(cart, expires_at: expired_at))
+
+ # Opening the checkout page calls extend_cart_expiry, sliding the window
+ # 15 minutes into the future regardless of where it was before.
+ {:ok, view, _html} =
+ live(conn, ~p"/events/#{event.slug}/checkout?cart_token=#{cart.session_token}")
+
+ refute render(view) =~ "expirou"
+
+ view
+ |> form("#checkout-info-form", %{checkout: %{name: "Alice Smith", email: "alice@test.com"}})
+ |> render_submit()
+
+ # Drain the push_patch to the summary step before proceeding
+ assert_patch(view)
+
+ view
+ |> element("#pay-bank_transfer")
+ |> render_click(%{"method" => "bank_transfer", "provider-id" => to_string(provider.id)})
+
view
|> element("#place-order-btn")
|> render_click()
- # The LiveView navigates after placing an order. Depending on whether a
- # payment provider is reachable (stubs may behave differently in test),
- # it can go to:
- # - /events/:slug/checkout/payment (push_patch for async payment step)
- # - /events/:slug/orders/:code (direct confirmation for free/instant)
- # - /events/:slug (fallback on payment error)
- # Any navigation away from the current page is the correct behaviour here.
- {path, _flash} = assert_redirect(view)
- assert is_binary(path)
+ # The order must succeed and patch to the payment step — NOT show a "cart expired" error.
+ path = assert_patch(view)
+ assert path =~ "/checkout/payment"
end
end
@@ -451,7 +516,7 @@ defmodule PretexWeb.EventsLiveTest do
{:ok, _view, html} =
live(conn, ~p"/events/#{event.slug}/orders/#{order.confirmation_code}")
- assert html =~ "Pedido Confirmado"
+ assert html =~ "Aguardando Pagamento"
assert html =~ order.confirmation_code
assert html =~ "Bob Builder"
assert html =~ "bob@example.com"
diff --git a/pretex/test/pretex_web/live/events_live/payment_processing_live_test.exs b/pretex/test/pretex_web/live/events_live/payment_processing_live_test.exs
index bb8b8fa..9f73ec9 100644
--- a/pretex/test/pretex_web/live/events_live/payment_processing_live_test.exs
+++ b/pretex/test/pretex_web/live/events_live/payment_processing_live_test.exs
@@ -402,7 +402,7 @@ defmodule PretexWeb.EventsLive.PaymentProcessingLiveTest do
# ---------------------------------------------------------------------------
describe "AC3 — redirect-based payment" do
- test "payment with redirect flow redirects the browser to external URL", %{conn: conn} do
+ test "payment with redirect flow redirects the browser to external URL", %{conn: _conn} do
org = org_fixture()
event = published_event_fixture(org)
item = item_fixture(event, %{price_cents: 5000})