Closed crypto-rizzo closed 2 years ago
Talking about the Database schema, I'm thinking about having three extra Models to deal with the checkout.
In addition, the Event Model must contain the payment method that will be accepted.
So we can define this use case: A User wants to buy two tickets from XPTO Event. This Event must be paid using asset ownership. Then:
This is an Example of flow that explain how the "Buy Tickets" should occur.
Other Model fields could be added to the Models to store info and validate the checkout status.
Here is a diagram that represents the relation between the Models
I think this method covers all possibilities of interaction between checkout, payment and tickets. I'm not sure about simplicity, but this is how a checkout solution is built on the market. With regard to robustness and flexibility, it will certainly meet the demands.
We could discuss any thoughts you all have and adapt whatever is needed.
Thanks for this @hubertokf. I think this flow is pretty close to what we're going to need, although I think there may be a few caveats.
The major problem I see with this exact approach is that payment_method
is stored as a field on Event
, which limits us to only one type of payment_method for an event. Additionally, our current ticket approach is pretty limited with only type of general admission ticket.
I think both of these problems can be solved here with supporting multiple ticket tiers.
Here's my suggestions based on review and some additional reading over the weekend:
TicketTier
: Defines each tier of tickets for an event. Holds information on capacity, limits, quantity sold, and supported payment method.CheckoutSession
: Defines a one-time, time-limited checkout session. This session contains the shopping cart items and other information required for checkout. CartItem
: Defines a ticket selection. This ticket selection is tied to TicketTier
, and stored via a reference to CheckoutSession
.Order
: Defines a completed checkout session. This contains attendee information. OrderTransaction
: Defines payment information for a specific Order
. (NOTE: I think we can probably forgo this separate model, and instead put these fields on the Order
table itself.)Ticket
: Same as before, but now tied to a specific Order
. Attached below is schema and related image.
CREATE TABLE "root_event" (
"id" bigint NOT NULL,
"created" timestamp NOT NULL,
"modified" timestamp NOT NULL,
"public_id" uuid NOT NULL,
"title" "character varying(255)" NOT NULL,
"description" text,
"start_date" timestamp,
"timezone" "character varying(30)",
"initial_place" "character varying(1024)",
"team_id" bigint NOT NULL,
"user_id" bigint,
"cover_image" "character varying(100)",
"end_date" timestamp,
"organizer" "character varying(255)",
"visibility" "character varying(50)" NOT NULL,
"is_featured" boolean NOT NULL,
"publication_date" timestamp,
"state" "character varying(50)" NOT NULL,
"address_1" "character varying(255)",
"address_2" "character varying(255)",
"city" "character varying(255)",
"country" "character varying(2)",
"lat" "numeric(9, 6)",
"long" "numeric(9, 6)",
"postal_code" "character varying(12)",
"region" "character varying(4)",
"localized_address_display" "character varying(1024)",
"show_team_image" boolean NOT NULL,
"show_ticket_count" boolean NOT NULL
);
CREATE TABLE "root_ticket_tier" (
"id" bigint NOT NULL,
"created" timestamp NOT NULL,
"modified" timestamp NOT NULL,
"public_id" uuid NOT NULL,
"event_id" bigint NOT NULL,
"ticket_type" varchar,
"payment_type" varchar,
"price" integer NOT NULL,
"capacity" integer NOT NULL,
"quantity_sold" integer NOT NULL,
"max_per_person" integer NOT NULL
);
CREATE TABLE "root_checkout_session" (
"id" bigint NOT NULL,
"created" timestamp NOT NULL,
"modified" timestamp NOT NULL,
"public_id" uuid NOT NULL,
"event_id" bigint NOT NULL,
"expiration" datetime NOT NULL
);
CREATE TABLE "root_cart_item" (
"id" bigint NOT NULL,
"created" timestamp NOT NULL,
"modified" timestamp NOT NULL,
"public_id" uuid NOT NULL,
"ticket_tier_id" bigint NOT NULL,
"checkout_session_id" bigint NOT NULL,
"quantity" integer NOT NULL
);
CREATE TABLE "root_order" (
"id" bigint NOT NULL,
"created" timestamp NOT NULL,
"modified" timestamp NOT NULL,
"public_id" uuid NOT NULL,
"checkout_session_id" bigint NOT NULL,
"name" varchar NOT NULL,
"email" varchar NOT NULL,
"cost" integer NOT NULL
);
CREATE TABLE "root_order_transaction" (
"id" bigint NOT NULL,
"created" timestamp NOT NULL,
"modified" timestamp NOT NULL,
"public_id" uuid NOT NULL,
"order_id" bigint NOT NULL,
"status" varchar,
"data" json
);
CREATE TABLE "root_ticket" (
"id" bigint NOT NULL,
"created" timestamp NOT NULL,
"modified" timestamp NOT NULL,
"public_id" uuid NOT NULL,
"order_id" bigint NOT NULL
);
ALTER TABLE "root_event" ADD FOREIGN KEY ("id") REFERENCES "root_ticket_tier" ("event_id");
ALTER TABLE "root_ticket_tier" ADD FOREIGN KEY ("id") REFERENCES "root_cart_item" ("ticket_tier_id");
ALTER TABLE "root_cart_item" ADD FOREIGN KEY ("checkout_session_id") REFERENCES "root_checkout_session" ("id");
ALTER TABLE "root_checkout_session" ADD FOREIGN KEY ("id") REFERENCES "root_order" ("checkout_session_id");
ALTER TABLE "root_order" ADD FOREIGN KEY ("id") REFERENCES "root_ticket" ("order_id");
ALTER TABLE "root_order" ADD FOREIGN KEY ("id") REFERENCES "root_order_transaction" ("order_id");
ALTER TABLE "root_event" ADD FOREIGN KEY ("id") REFERENCES "root_checkout_session" ("public_id");
What do you think @nftylabs/bix @halfmoonui?
Got your idea! I have a few questions that I want to ask you:
TicketTier
:
CheckoutSession
and Order
: Taking a look into this two models, they are very close. What is the difference between these two models? Could it be one where a completed status could be defined by another field on the model? Why is it linked with the Event?OrderTransaction
: One order could have more than one transaction? If so, we can't remove this model.Taking a look on this models, here is my suggestion:
To answer your questions directly @hubertokf :
TicketTier
Agree on this point. Like you said, I think we just add a 'completed' field to the CheckoutSession
, and forgo the Order
model.
I think this is more of a question for the checkout flow itself. Should the checkout flow for attendees support MULTIPLE payment methods in one go? (i.e., I buy 2 tickets with asset ownership and 3 tickets with fiat)? For simplicity sake, my initial idea was to restrict ticket selection to tickets with the same payment method. (If you select asset ownership, you cannot select any tickets that require fiat). However, if we want to support multiple payment methods, then like you said, an Order
can potentially have more than one transaction.
I think there a few big questions remaining below
I can't answer the first two questions. But the last one, we could handle using the time-limited checkout session as you mentioned before. Once it is created, it will subtract the amount from the Ticket Tier. And return it if the time expires.
It is kind of tricky because we have the quantity sold stored on the Ticket Tier Model, but we could handle doing (quantity sold + open checkouts * quantity) on the go and storing the quantity sold once the checkout is finished.
At @hubertokf's first model diagram:
Can't we just merge Checkout
model into the CheckoutEvent
and store the user ID (foreign key) on there instead? Or is it because you're thinking about each Checkout
object potentially having more than one CheckoutEvent
?
At @crypto-rizzo's model diagram:
Again, I think we can merge some of the models - Order
, OrderTransaction
, and CheckoutSession
are good candidates depending on how we decide to design the flow.
The way this is being planned, I do worry that we would have to rely on database transactions quite a bit, and that can cause performance issues. Would love to hear both of your thoughts on this because I'm certainly not an expert on this.
To try and answer the first two big questions:
I don't fully understand this question. Is it whether or not we give users multiple payment options in the order and they pick one? Or is it that users would be able to use more than one payment method to pay for one order (let's say I pay once using my card, and then using my NFT)? If it's the latter, then I don't think this is a good idea. While this would undoubtedly make our product super flexible in terms of functionality, practically speaking, it would be very very hard to design a good UX that accommodates this type of flow.
I think the answer to this would depend on the first question and what we decide to do there, unless I'm misunderstanding something.
Converting @hubertokf's diagrams into Markdown/Mermaid:
classDiagram
class Checkout {
+int id
+uuid public_id
+datetime created
+datetime modified
+int user
}
class CheckoutEvent {
+int id
+uuid public_id
+int checkout
+int event
+int quantity
+datetime created
+datetime modified
+int user
}
Checkout <|-- CheckoutEvent
class Transaction {
+int id
+uuid public_id
+int checkout
+varchar type
+json data
+float value
+varchar status
+datetime created
+datetime modified
}
Checkout <|-- Transaction
After thinking about this issue, I don't think I can come up with UML diagrams that would be significantly better/different from the ones already posted here. However, I would like to put more emphasis on the UX aspect of this whole issue. As in it doesn't really matter to the customer how we make our database work, but we really need to nail down the product design for this flow to make it as easy and smooth as possible.
I've went ahead and created a new UML diagram based on the feedback received.
Most notably, there are now three separate Transaction
models - fiat_tx
, blockchain_tx
, and asset_ownership_tx
.
The idea here is that each of these flows has vastly different information required for checkout. Additionally, since we plan to constrain UX based on the payment method, it makes sense to split up this data on the backend.
Additionally, Order
and OrderTransaction
have had their fields split into these Transaction models, as well as the CheckoutSession
model.
Below is schema and diagram again.
Table "root_event" {
"id" bigint [not null]
"created" timestamp [not null]
"modified" timestamp [not null]
"public_id" uuid [not null]
"title" "character varying(255)" [not null]
"description" text
"start_date" timestamp
"timezone" "character varying(30)"
"initial_place" "character varying(1024)"
"team_id" bigint [not null]
"user_id" bigint
"cover_image" "character varying(100)"
"end_date" timestamp
"organizer" "character varying(255)"
"visibility" "character varying(50)" [not null]
"is_featured" boolean [not null]
"publication_date" timestamp
"state" "character varying(50)" [not null]
"address_1" "character varying(255)"
"address_2" "character varying(255)"
"city" "character varying(255)"
"country" "character varying(2)"
"lat" "numeric(9, 6)"
"long" "numeric(9, 6)"
"postal_code" "character varying(12)"
"region" "character varying(4)"
"localized_address_display" "character varying(1024)"
"show_team_image" boolean [not null]
"show_ticket_count" boolean [not null]
}
Table "root_ticket_tier" {
"id" bigint [not null]
"created" timestamp [not null]
"modified" timestamp [not null]
"public_id" uuid [not null]
"event_id" bigint [not null]
"ticket_type" varchar
"payment_type" varchar
"price" integer [not null]
"capacity" integer [not null]
"quantity_sold" integer [not null]
"max_per_person" integer [not null]
}
Table "root_checkout_session" {
"id" bigint [not null]
"created" timestamp [not null]
"modified" timestamp [not null]
"public_id" uuid [not null]
"event_id" bigint [not null]
"expiration" datetime [not null]
"name" varchar [not null]
"email" varchar [not null]
"cost" integer [not null]
"status" varchar
"fiat_tx" bigint [null]
"blockchain_tx" bigint [null]
"asset_ownership_tx" bigint [null]
}
Table "root_checkout_item" {
"id" bigint [not null]
"created" timestamp [not null]
"modified" timestamp [not null]
"public_id" uuid [not null]
"ticket_tier_id" bigint [not null]
"checkout_session_id" bigint [not null]
"quantity" integer [not null]
}
Table "root_ticket" {
"id" bigint [not null]
"created" timestamp [not null]
"modified" timestamp [not null]
"public_id" uuid [not null]
"checkout_session_id" bigint [not null]
"ticket_tier_id" bigint [not null]
}
Table "root_fiat_tx" {
"id" bigint [not null]
"created" timestamp [not null]
"modified" timestamp [not null]
"public_id" uuid [not null]
}
Table "root_blockchain_tx" {
"id" bigint [not null]
"created" timestamp [not null]
"modified" timestamp [not null]
"public_id" uuid [not null]
}
Table "root_asset_ownership_tx" {
"id" bigint [not null]
"created" timestamp [not null]
"modified" timestamp [not null]
"public_id" uuid [not null]
}
Ref: "root_ticket_tier"."event_id" < "root_event"."id"
Ref: "root_checkout_item"."ticket_tier_id" < "root_ticket_tier"."id"
Ref: "root_checkout_session"."id" < "root_checkout_item"."checkout_session_id"
Ref: "root_ticket"."checkout_session_id" < "root_checkout_session"."id"
Ref: "root_fiat_tx"."id" < "root_checkout_session"."fiat_tx"
Ref: "root_blockchain_tx"."id" < "root_checkout_session"."blockchain_tx"
Ref: "root_asset_ownership_tx"."id" < "root_checkout_session"."asset_ownership_tx"
Ref: "root_ticket"."ticket_tier_id" < "root_ticket_tier"."id"
Ref: "root_checkout_session"."event_id" < "root_event"."id"
Totally agree on the UX aspect as well. Below is roughly the user flow I was thinking, based on this DB schema.
CheckoutSession
).TicketTier
s. TicketTier
. (NOTE: Users can request tickets from multiple TicketTier
s, but ONLY if they use the same payment_type
). CheckoutItem
, with a reference to their CheckoutSession
.payment_type
of the ticket, generate a corresponding *_tx
model to be used in checkout.*_tx model
(sign message, pay fiat, or send crypto). Once this *_tx` model has met all the requirements for checkout, proceed to success. Create and deliver tickets to user.@hubertokf on high-level user story (what we are trying to accomplish) @halfmoonui on low-level user story (screens, flow, etc.) @crypto-rizzo on ERD
Deadline 9/23
User Story:
As an event attendee, I want to buy tickets using fiat, crypto, and/or NFT ownership, so that I get to attend the events I want.
Acceptance Criteria:
Let me know if this what you meant by a user story @crypto-rizzo
Also posting the rough wireframes here before moving to the actual UI:
Each of the shaded box would look something like this:
@mvpedro @devjoaov ⏫
@halfmoonui I like what you did there but have a couple of thoughts that I liked to share:
1- Wouldn't we deliver too much information at the same time to the user by grouping the every ticket tier available to each of the accepted currencies on the same page? Following this approach we could end up having 12 or more options for the user to click on the same screen (4 payment options X 3 ticket tiers). With each of the shaded boxes having at least 4 different infos (ticket tier, amout left, price, quantity) it could mean that prior to ticket selection we could reach (following the same logic on the previous question) close to 48 infos on the screen. Wouldn't it be too cluttered? What I've seen Event companies do is that they split ticket selection from payment selection in two screens (like Eventbrite on the attached screenshot). I think it could be a more seamless process to the user if we had a page with only the ticket tiers and then proceed to the accepted currencies.
2- On a more general note, our current solution seems to contemplate events that have different entrance gates but not different individual spots (such as numbered chairs on a theater). Should we adapt our design to also fit that kind of possible client as well? From a product validation standpoint I think we should design a solution that validates the product and not every possible client needs but it could be good to wonder if we should already adapt things so that can be an easier implementation in the future.
Newest ERD / Schema https://dbdiagram.io/d/6321f1240911f91ba5ae16a2
![Uploading SocialPass V1-10 (1).png…]()
I think discussion is pretty much wrapped here - should we close @hubertokf ?
I think so
Theme
V1 will revolve around overhauling our core business model. More specifically, we will pivot from charging event organizers to monetizing the ticket checkout experience.
The ideal flow will support a mix-and-match variety of these 4 ticket options: free, fiat, crypto, and asset ownership.
What is the situation
This will require a comprehensive rethinking to our current approach, starting with the database schema and finishing with the end-user experience. Components impacted will include
Key questions include
What is your opinion
I think we need to do a really good job of splitting up research and assignments here, whilst still working closely together. A few working calls this sprint seems like a good idea.
On the checkout experience, I'd say my priorities are in this order