Jump to content

SaranacLake

Members
  • Posts

    648
  • Joined

  • Last visited

Posts posted by SaranacLake

  1. Hello.  I could use some advice on the best way to handle my shopping cart.

    My website will sell subscriptions along with more traditional items like books, t-shirts, etc.

    For subscriptions, I have these business rules...

    1.) Only non-members can purchase a "trial subscription".

    2.) Once you are a "member", then no more trials for you!

    3.) You can purchase multiple subscriptions (e.g. into the future), BUT I only allow you to purchase ONE subscription at a time.  (My system would allow multiple purchases at once, but I don't ant people accidentally signing up for 2 years and then i have to clean things up.  Also, I want the transition from a "trial" to a "full paid" subscription to be clear.)

    4.) if I ever offer multiple "trial" offers, then you only get to take advantage of one, and then after that it's on to full paid subscriptions for you - although I will run "promos".

     

    Here is the problem...

    Nothing stops a non-member (or a member) from adding a "paid" and a "trial" subscription into their shopping cart.  Or multiple "trial" subscriptions.  Or multiple "paid" subscriptions.  (Maybe someone adds a paid subscription on Friday, then the weekend comes, and on Monday they notice the trial subscription offer and add that forgetting about the paid subscription.)

    Or maybe someone is trying to "game" things, and adds 3 "trial" subscriptions and tries to check out?!

     

    How should my website (gracefully) handle these scenarios?

     

    1.) Am I aggressive up front, and prevent people from adding a 2nd subscription to their shopping cart as they shop?

    2.) Or do I allow them to go wild while shopping, but then address the issue at checkout?

    3.) Should my code be doing the work, or is the onus on the user to go in and clean up their shopping cart?

    4.) If I address this issue at checkout, then do I need to add extra functionality to adjust their shopping cart from the checkout page on-the-fly?  Or do I send them back to their actual shopping cart page and make them clean things up there?

    5.) What do I do if someone has already added other items to their cart previously (e.g. T-shirts).  Do I handle the "Get this offer" separately hoping to get them signed up ASAP, and then leave the T-shirts in their cart to purchase later?  Or do I handle everything at once?  (If they just have a "trial" subscription and a T-shirt, no worries.  But if they have a "paid" subscription and then add a "trial" subscription and are looking for a quick checkout, then the scenarios/concerns above come into play...)

     

    The stuff I was asking about last night is what got me to thinking about all of this.

    The flow was supposed to go...  User sees "get one month for $1" on the home page, clicks the button, gets a trail-offer details paid, clicks "Get this offer", and wah-lah, they are on the checkout page!  But if they already have things in their cart or maybe click the offer button 10 times, then things get messier.

    My concern is that converting a user to a paid member is the #1 goal, and I don't want other things to get in the way, whether that is a complicated checkout or things getting distracted purchasing other things like T-shirts!  At the same time, if you want to buy multiple things, all the better as long as you follow my business rules.

     

    So what's the best way to not be a pain to shoppers, but to maintain the rules I mention above?

     

  2. @kicken 

     

    5 minutes ago, kicken said:

    It's just a normal HTTP request, just like anything else.  Forms really are not anything special.  It's just like a link using an <a> tag, except you can send more data by using method="post".

    So if your trial page outputs the form

    
    <form method="post" action="checkout.php">
      <input type="email" name="Email" value="example@example.com">
      <button type="submit" name="ProductId" value="1">Get trial</button>
    </form>

    When you click the button the browser will

    1. Open a connection to example.com
    2. Submit the http request
    
    POST /checkout.php HTTP/1.1
    Host: example.com
    Content-type: application/x-www-form-urlencoded
    
    Email=example%40example.com&ProductId=1

    If you don't understand the above, you should do some research on the HTTP protocol and learn how it works.  As you can see though, your trial page is not referenced at all in those steps.  The request is for checkout.php and the data is sent in the body of the request in the same format as a query string.

    Okay, thank you, that is helpful!

     

     

    5 minutes ago, kicken said:

    This is not at all true.  POST data is just as visible and open to manipulation as GET data.  The only difference is where it's located in the request. This is why I said you need to get over your trust issue with $_GET. 

    Okay, point taken.

     

     

     

    5 minutes ago, kicken said:

     $_SESSION is the only thing you can generally trust as that data never leaves the server and cannot be manipulated directly by someone, they would have to exploit some vulnerability in your server or your code.

    Yeah, in the past I relied more heavily on $_SESSION, although with any data, I always try to check it against the database to make sure it wasn't manipulated!

     

     

    5 minutes ago, kicken said:

    It's a little bit of a semantics / being pedantic thing.  Big picture, sure you are passing some data.  Your not passing the trial pages $_POST array though.  The trial page doesn't have a $_POST array unless your posting data to it in some way.  By adding the action attribute pointing to checkout.php you're no longer posting any data to the trial page, your posting it to the checkout page.  

    I welcome being corrected on semantics, and "pedantics" makes for better programmers!  🙂

    Based on what you said above, I guess what is happening is that the user requests trial-offer.php and the server serves that page up to the user's browser which in turn displays the web-form.

    When the user submits the web-form on trial-offer.com, it's not like data is being copied from trial-offer.php to checkout.php, but rather that the user is requesting checkout.php AND is appending some data on the end of that request - similar to a $_GET query - and so that USER-DATA gets sent to checkout.php but it's not like you have intra-webpage data transfer like I made it sound...

     

    How does that sound?

     

    Likewise, it sounds like the $_POST array doesn't really come into the picture until after checkout.php has received the USER DATA.  Is that correct?

     

     

    5 minutes ago, kicken said:

    There's nothing wrong with using terminology like "pass the ProductId to the checkout.php page", but you should have the understanding of how that actually works.  The language in your posts implied that you did not as you seemed to think that PHP was either sharing one $_POST between scripts (misunderstanding "global" I think) or somehow moving the $_POST array from one script to another which is not how it works. 

     

    Yes, I was WRONG in both conceptions of how things actually work!!

     

    Thanks for helping to clarify those subtle, yet important differences!!

     

     

     

  3. @kicken

    3 minutes ago, kicken said:

    Based on your last post it sounds to me like you still don't really understand the process, or maybe you do but just don't articulate it well.  It sounds to me like you're thinking you are defining $_POST['ProductID'] on your trial page then have to somehow transfer that variable over to the checkout page.

    You may be correct in that I don't understand things...

    I thought that if I created a form on trial-offer.php, that when you submitted that form - which in my case would likely just be a hidden field containing the "ProductD" - that things got put into the $_POST array like $_POST['ProductID'].

    And based on what @requinix  said, I thought the scope of the $_POST array was local to, in this case, trial-offer.php.

     

     

    3 minutes ago, kicken said:

    That's not really how things work though.   Your trial page doesn't have a $_POST['ProductID'] variable.  It generates a form as it's output.  That form is submitted to the checkout page

    On a side note, how is the form data transferred to action="checkout.php"?

    I was always under the impression that POST data was basically hidden and safe from the outside world, including hackers...  And I guess I thought the $_POST array somehow securely "wrapped" your submitted form data and sent it to its end destination.  (Then again, since as long as I have been using PHP, I just submit forms to themselves, I guess I haven't had to really think how things work!!)

     

    3 minutes ago, kicken said:

    , and that form's data contains a ProductID field which makes it so that your checkout script will have a $_POST['ProductID'] variable.

    Let me repeat...

    So if I do like in the past and have...

    	<form id="" action="" method="post">
    	

    then every field on my form gets submitted back to the same calling script (e.g. "trial-offer.php"), and all of that data gets nicely packaged into a $_POST array that is *only* accessible to - in this case - "trial-offer.php", right?

     

    But if my action statement was action="checkout.php" then you are saying that all of the form data gets sent to - in this case - "checkout.php" and the data is wrapped in a nice $_POST array that is *only* available to - in this case - "checkout.php", right?

     

     

    3 minutes ago, kicken said:

    Taking a step back to basics again, every request you have to look at in complete isolation.  The request has to contain whatever data your script needs to get stuff done, and there are a variety of places in a request that the script can look at for data.

    $_GET - Data from the requested URL's query string (side note, you should get over your trust issues, $_GET is fine for something like a product ID).
    $_POST - Data from the request's body content, assuming the body is form data.
    $_COOKIE - Data from any Cookie: headers
    $_SESSION - Session data is handled via a slightly more complex process.  When your script ends, the data stored in $_SESSION is saved to the web server somewhere and a unique identifier is then passed to the client.  The client passes that identifier back to PHP on the next request (usually as a $_COOKIE value) and then the data is looked up on the server and restored.

    Okay, yes, I vaguely remember these things, although I worked almost entirely with $_POST and $_SESSION in the past.  (A little $_GET, but never $_COOKIE).

     

     

    3 minutes ago, kicken said:

    Whether you use a database or not doesn't really matter.  You'd give your checkout.php page an identifier of some sort via one of the above mentioned input options.  Your checkout page would then use that input to do whatever it needs to do.

    Yes, understood.

    Because I am most comfortable with databases - and I think they are safer as far as not losing data/maintaining control in relative terms - I was thinking of doing this...

    	- User is on home page
    	- User clicks the "One month for $1" button
    	- System loads "trial-offer.php"
    	- User clicks "Get this offer"
    	- System submits hidden field containing ProductD = 1234 via the form back to trial-offer.php
    	- trial-offer.php writes this to the shopping cart table.
    	- trial-offer.php redirects to checkout.php
    	- checkout.php finds the SessionID and queries the shopping cart table for the user's shopping cart items (e.g. trial offer)
    	- checkout.php populates the shopping cart items onto the checkout pages and builds the rest of the checkout page
    	- and so on...
    	

     

    My earlier questions to @requinix was more just to learn/re-learn how I might do the same thing by merely "passing" the ProductID directly from trial-offer.php to checkout.php if I didn't want to use my database here.

    And clearly my understanding of how forms and $_POST works were off!!  🙂

     

     

    3 minutes ago, kicken said:

    You can provide your identifier via $_POST if you want to checkout.php, but you're not "transferring $_POST['ProductID']" from the trial offer script to your checkout script, at least not in the way it sounds to me like you are thinking.  

    Your trial offer script does nothing more than generate a form with whatever data you need.  Once that is generated and sent to the client the trial offer script is done and is no longer relevant.  When the user submits that form then the browser would take the form's data and send it directly to checkout.php, no other script is involved in the process.

    Okay this is where I clearly didn't understand (or remember) things...

    So it sounds like you are saying that even though the user is ON "trial-offer.php", because I have defined action="checkout.php" it is like trial-offer.php isn't even there, right?

    In this case, I think you are saying that my trial-offer.php form data - whether that be one hidden field or 20 name/address/interest fields - gets shipped over imemdiately to checkout.php in a nice container of the $_POST array, but it's all about checkout.php at that point, right?

    (Again, I have spent my entire short PHP career submitting forms/PHP pages back to themselves, so that sorta *warped* my understanding/view of how things were working...  Like when you log in on my website, I load login.php which displays the form, you enter your credentials, then click "Log in" and that form data gets sent right back to login.php.  From there I dissect $_POST and compare the $_POST['username'] and $_POST[password'] or more like hash against the database and then my login.php script either logs you in and redirects you or you get an error message on login.php!)

     

    Submitting a form on Page-1 and letting Page-2 is an entirely new concept to me - as embarrassing as that may be?!  🙂

     

    3 minutes ago, kicken said:

    Now, going back to your original question, you mention having a shopping cart, so you need to split your process into two parts. 

    1. Add the item to your cart. 
    2. Display the items in your cart.

    Whether you want to do that via two separate scripts or all in your checkout.php doesn't matter too much.  

    You could have your trial offer page post back to itself like your normally do and add the item to the cart then just redirect.  In that case your checkout doesn't need to do anything other than look at the contents of the cart and display them.

    Right.  if I add the chosen item - in this case the "Trial Offer" - directly to the database from trial-offer.php then I could do like I have always done.

    And as you say, just let checkout.php load the shopping cart item(s) when that page loads - which it would upon being re-directed to from trial-offer.php.

     

    Again, i was just curious how I could "pass" ProductID to checkout.php without using MySQL.

     

    Earlier I think you said that trial-offer.ph would not "pass the $_POST array" to checkout.php, BUT you did make it sound like trial-offer.php DOES pass all of the form data to checkout.php because I would have action="checkout.php"

     

    Can you help me better understand the "mechanics" and proper language to describe what does happen getting data from point-A to point-B?

     

    This is an interesting discussion!!  👍

  4. 24 minutes ago, requinix said:

    Sorry but no, it is not what you thought. Seems a quick primer is in order.

    PHP handles exactly one page request at a time. When it finishes with a page, it completely forgets everything that happened. You can't set a variable on one page and get it in another page. Obviously that would be a huge limitation to making a website work, and that is why things like databases and cookies and sessions exist.
    That means there is no transfer of "control" between pages. PHP doesn't remember that the user was on trial-offer.php and is now going to checkout.php. All it knows is that someone is trying to request checkout.php, and they may or may not be sending data too.

    Thanks for the refresher - I told you it's been a l-o-n-g time since I've done PHP!

    So in my case, when the user clicks on "Get this offer" on the trial.offer.php page, if I wasn't using my database to store things, then how would I send the ProductID - corresponding to whatever trial offer exists at that the time and hard-coded into my page - to the checkout.php page so it knows what the user wants to buy?

    You are saying I can't use $_POST to transfer that information from trial-offer.php to checkout.php.

    And I only use cookies to store the SessionsID.

    So would I have to "pass" the ProductID in the $_SESSION variable?

    (And I thought e-commece sites often used query strings to pass data, or is that just to request data like in a product search?)

     

    24 minutes ago, requinix said:

    $_POST is not a database or cookie or session. It's a variable. A variable that you can reference from anywhere in your code, but just like every other variable, it's only usable for that one page. If you made any changes to it (which is a bad practice so don't do that) then you could see those changes for the rest of the page, but whatever you do will not be available on another page. Same for $_GET.

    $_GET gets its information from the query string - the stuff after the question mark in the URL. PHP looks at the query string, parses it, and shoves the data into $_GET for your convenience.
    $_POST gets its information from POSTed form data. The most common way to send POSTed form data is with a <form> whose method=POST. PHP looks at the data sent to the server, parses it, and shoves the data into $_POST for your convenience.

    Apparently in the past I was able to do pretty much everything I needed using one web page at a time....

     

    24 minutes ago, requinix said:

    If you have a form with an action="checkout.php" then the browser doesn't have to assume anything, and it will tell the server that it wants checkout.php with some POSTed data.

    I'm confused by that last statement...

    If I have a form on the trial-offer.php page and it has action="checkout.php" then it will tell the server it wants checkout.php and pass along the $_POST array?  (Because when you end that with "...with some POSTed data" that implies you are passing along the $_POST array from one web page to another.)

    Is that what you meant?

     

     

     

     

     

     

  5. 2 minutes ago, requinix said:

    That's fine, but a product and a subscription are not the same thing. You treat one differently than the other, not just internally but with the user experience too. And that distinction matters.

    This relates back to my debate with Gizmola a few months ago.  I decided to have a super-type Product table and then sub-type tables like Membership_Plans, eBooks, Gear.

    So there will be a productID for anything you buy for me.  And yes, a subscription is obviously treated different than say a T-short.

    But as far as the shopping cart is concerned (or the checkout page), everything is referenced by its productID.

     

     

    2 minutes ago, requinix said:

    But I take back what I said earlier. It's okay to add products things within checkout.php. It makes sense as a single click operation: user wants thing, doesn't want anything more, goes to checkout which also adds that thing to the cart.

    For subscription, when a user chooses one I go directly to the checkout page since I want to get them to become a member before they change there mind.  So that is sorta like a "Buy it now" button which I think you are referring to.  (I also offer that option on the product details page for things like T-shirts.  ("Add to Cart" or "Buy it Now")

     

     

    2 minutes ago, requinix said:

    If you want.

    Yeah, in reflection, I prefer working from the database when possible.

     

    2 minutes ago, requinix said:

    You're comparing apples and oranges. The database is the "source of truth" regarding what is currently in the shopping cart. It is not the source of truth for how things get into that cart.

    True, but my point was it is easier to work from one source (i.e. shopping cart) versus working with a variable and the database and keeping them in synch.

    If you add items to the database (i.e. Add-to-cart), then read from the database (checkout) then you never get out of synch.

     

     

    2 minutes ago, requinix said:

    That is not at all what they mean.

    You know how variables defined outside a function are not available inside a function? The $_GET and $_POST (and other) superglobals do not have that restriction.

    That is what I thought.

    Okay, so let's say trial-offer.php has this code...

    	<form id="trial-offer" action="checkout.php" method="post">
    	<input id="" name="offer" type="text" value="mp1234" />
    	

    (Hopefully I remembered the correct syntax above)

     

    When the use clicks "Get this offer" in "trial-offer.php" then that chocie gets put in the $_POST array.

    And I guess you are saying by virtue of using a gloabl variable like $_POST that my other script, "checkout.php" can automatically reference that variable, right?

    If so, then what does action="checkout.php" accomplish other than sending control from "trial-offer.php" to "checkout.php"?

     

    Sorry, I haven't actively coded in PHP in like 5 years...  😑

     

    2 minutes ago, requinix said:

    There should never be any sending of POST data between your own PHP scripts.

     

  6. 3 minutes ago, requinix said:

    I don't recommend using the checkout page as a way to add any product. Don't misunderstand: I'm not saying this subscription business should go in the checkout page. I'm saying it can go there - for your own ease of use.
    (The alternative is having an AJAX-y way of adding products, then when that request completes the button navigates to the checkout page.)

    Maybe what I said wasn't clear enough.

    To my a ProductID and SubscriptionID are the same thing, although if you buy a T-shirt that would only be a ProductID - I am using the terms interchangeably here.

    Also, my checkout page has a summary of all items in the shopping cart at the top - it's all one page.

    So originally I was thinking of having trial-offer.php send a Product/SubscriptionID to the checkout.php script where it adds things to the shopping cart AND then reads the shopping cart to populate the "cart summary" portion used during checkout.

    The more I think about things - and the way I have always done things in the past - I think I will have trial-offer.php write the SubscriptionID to the shopping-cart table, then redirect to checkout.php, and then checkout.php can read the shoping_cart table and populate itself.

    "Six of one, a half-a-dozen of another", but I always prefer using the database as the "source of truth" so I wouldn't need to pass a Subscription/ProductID between web pages that way.

     

    3 minutes ago, requinix said:

    It would submit the contents of the form to checkout.php instead of to the current URL.

    Before you continue, you should probably familiarize yourself with HTML forms and how they interact with the server. That's some rather important knowledge to have.

    I will read up on things, but aren't the $_POST and $_GET arrays "global" so that when you submit a form from page-1.php it writes things to say $_POST and then page-2.php can automatically read $_POST?

    Or does page-1.php physically pass the $_POST array to page2.php?

     

     

  7. 8 minutes ago, requinix said:

    Having your checkout page support form data to add the offer does kinda make sense. You can do that.

    Well, my checkout page will of course support form data since that is where the user enters in payment details, etc.

    What I was asking about is sending the ProductID to the checkout page so it can add things to the shopping cart and then use it for checkout.

     

    8 minutes ago, requinix said:

    If you have a page listing your subscription(s) then surely you have something to go with it like a subscription ID? Pass that to the checkout page. POST would be better since you're talking about something that modifies data.

    Okay, that is hat I was asking about.

    So the whole time I have been using PHP, when I submit a form I always do this...

    	<form id=login" action="" method="post">
    	

    which in turn sends control back to the same script.

     

    If I anted to pass a ProductID/SubscriptionID from the trial-offer.php page to say my checkout.php page, is it as simple as tweaking the above code to say this?

    	<form id=login" action="checkout.php" method="post">
    	

     

    And what exactly would that do?

    Would I be physically sending the $_POST array to the heckout.php script, or is that revised code just sending control to the checkout.php script?

    (Those are the parts I am unsure of since I have only ever worked with a single script to handle forms up until now.)

     

  8. Hello.  This is sort of an embarrassing question, but I guess it is something I've never done before.

    In the past, whenever I had a command button on a webpage, I used a form and when the user clicked on the button I also just reloaded the same PHP script to process the $_POST request.  (I realize that a lot of people have the form on the 1st page/script, and the have a 2nd page/script to handle the form request, but that always seemed like overkill to me.)

     

    With my new problem, I have one web page that has a subscription offer on it, with just some bullet points of why the user should be interested, and then a "Get this offer" button.

    When the user chooses this button, what should happen behind the scenes is that I add the ProductID to the shopping cart - which is a database record - and then I redirect to my checkout page.

     

    While typing up this thread, maybe I don't have an issue after all?  😕

    I guess what i could do on trial-offer.php is have a form surrounding my "Get this offer" button, and when the user submits the form, my trial-offer.php script could add a shopping_cart record in the database and then I could use a re-direct to go to my checkout.php script, right?

     

    But to my original question, if I did want to pass the ProductID to my "Checkout.php script, what would be the best way to do that?

    Could I still use a $_POST and but just send the form to my other script (i.e. checkout.php)?  (This is the part I was unsure of above!)

    Or would I be forced to use a $_GET which i don't really like or trust?

     

  9. @requinix

    Oops!  Looks I misspoke in my last post.

    I was correct that allow a Null member_id addressed what this thread was about.  But I now see what you were saying...

    I guess my ERD should look like this...

    MEMBER -|0------0<- SHOPPING_CART ->0------||- PRODUCT
    


     

    xxx

        That is because my Shopping_Cart would look like this...
        
       

        ID    SESSION_ID    MEMBER_ID    PRODUCT_ID
        1                    33              5027
        2                    33              9402
        3                    33              3371
        4    SESS_111                        8269
        5    SESS_111                        9077
        

     

     

    xxx

     

     

     

  10. 1 hour ago, requinix said:

    Yup.

    If you go to a grocery store to pick up milk and eggs, do you get one shopping cart for the milk and another shopping cart for the eggs?

    I don't think you understood my question above.

    Regardless of my question, the design I described above could have one or many records, but they would all be tied to either the same "member_id" OR to the same "session_id", thus constituting ONE shopping_cart per person.

     

    Just did a test, and since SHOPPING_CART.member_id is set to allow Nulls, I was able to insert a shopping_cart item/record WITHOUT needing to have a Member.

    So I think that allowing Nulls on member_id is what gives me the "zero" in "A Shopping_Cart can have zero to one Members..."

     

    Does that seem like an okay design?

  11. I have the following logical ERD design...

    	MEMBER -|0------0|- SHOPPING_CART
    	

     

    And here are the (abridged) tables...

    	MEMBER
    	- id (PK)
    	- username (UK)
    	- email (UK)
    	

     

    	SHOPPING_CART
    	- id (pk)
    	- session_id (UK)
    	- member_id (FK1)(UK)
    	- product_id (FK2)(UK)
    	- cart_price
    	- quantity
    	

     

    The logic is this...

    1.) A Member can have zero or one Shopping_Carts.  (As a Member, maybe you're shopping, and maybe you aren't.)

    2.) A Shopping_Cart can have zero or one Members.  (If a Member is shopping, then they would have a Shopping_Cart tied to their "id".  But if you are a new shopper, and not yet a Member, then the Member record doesn't exist, so there is no linkage to the Member table.  However, I am storing the SessionID in the Shopping_Cart so I can still link it to you.)

     

    Modeling this logically is easy enough, but I'm not sure of the physical implementation.

     

    I created a FK Constraint last night on on SHOPPING_CART.member_id as shown in (FK1) above, but won't that prevent me from placing an item into the shopping cart if there is not a corresponding Member??

     

  12. 9 hours ago, requinix said:

    *puerto rican sad face*

    I knew you were going to say that!  *LOL*

    (Until they figure out where to put that 51st star, it'll never happen!)  😉

     

     

    Quote

    "Abbreviation" or "Abbr". Please.

    Okay.

     

    Quote

    How is it wasteful? In this particular example, the state abbreviation is short and so I wouldn't mind it being the primary key (in which case don't have an ID column at all),

    Okay, good.

     

     

    Quote

    But the abbreviation and full name are both "lookup"-able values.

    Sure.

     

     

    Quote

    BUT I would not make a table that has only US states. I would pretty much always have to think about Canada, or maybe Mexico, or Australia, or some of the other countries that have states/provinces. In which case the abbreviation is no longer unique by itself - you'd need a country FK, and while country + abbreviation would be unique, I would not have the two act as the primary key (mainly due to the awkwardness of a composite PK).

    True, but since for now I am only accepting customers in the U.S. that won't apply.

    (It might be too hard to allow Candian customers, but I'm not even sur ethat my payment processor allows payments from overseas, but I see your point.)

     

     

    Lastly, so let's say I have another lookup table where I could use the lookup value itself as the PK - because it isn't too long and wouldn't likely change.

    As mentioned in my OP, I like have an auto-increment on all tables because it is easier to find records manually looking in the table or maybe if you exported on a report by looking for a numeric ID. 

    Would you rather scan a table looking for this...

    	SKU (PK)
    	MM1100
    	MMI100 <== That's an "I' (eye)
    	MN1100
    	MMIIOO <== Some Oh's there
    	

     

    Or...

    	ID (??)		SKU	(PK)
    	1		MM110
    	2		MMI100
    	3		MN1100
    	4		MMIIOO
    	

     

    Even if the ID's aren't sequential, it is just way easier to work with an ID as a human! 

    (Let MySQL have it's SKU as the PK, but for me I'd prefer an ID...)

     

    So, isn't there a way that I can have my "ID" but use something else as the PK and still not commit a mortal sin??

     

     

  13. 9 hours ago, requinix said:

    The length of the session ID isn't even remotely as important as what you do with your sessions in code. Focus on that instead.

    When I was searching last night for what the length might be, and where to find it, I saw an article by OWASP saying that your session id should be at least 128 bytes long to prevent against brute-force attacks, although I didn't read the article.

    Would it hurt to change my php.ini file to have session.sid_length = 256 ?

     

  14. For PHP 7.4, what is the recommended session length that I should be using to have the best security?

    In DEV< in my php.ini file, session.sid_length = 26

    According to the comments above that, it sounds like you can go up to 256.

    Is it fair to assume that larger is more secure?

    Also, if I set it to a larger size like 256, is tehre any risk of breaking things on my (VPS) web server running WHM/cPanel?

  15. @requinix

    I sorta figured you'd say what you said.  😉

    Okay, so let's say I have a lookup table that will NOT be changing any time soon (e.g. U.S. States).

    For brevity, could my lookup table just have ONE field called "slug" (or maybe "name") that lists the state abbreviations?  (i.e. AK, AL, AR, AZ,...)

    The logic being for simple lookups like that, it seems like a waste to have columns like: ID, slug/abbreviation, full-name, etc

    (In that case, the ONE column would be the PK!)

     

  16. Hello.  I have some questions related to setting up a Lookup Table.

     

    In the past, I set things up like this...

    	ARTICLE
    	- id
    	- title
    	- category (FK)
    	- body
    	

     

    	CATEGORY (lookup table)
    	- id (UK)
    	- slug (PK)
    	- name (UK)
    	

     

    	article.category ->0------||- category.slug
    	

     

     

    Here is the logic used on the above tables...

    a.) Every table in my database has an "ID" that is an auto-increment, and usually the "PK".

    b.) For my lookup table, I made "slug" the "PK" because I wanted an *English word* (e.g. "database-development") showing up in the main table.

    c.) As such, I made the "ID" a "UK" to ensure it is unique.

    d.) "Name" is just a pretty version of "slug" in case I need to display it or something like that (e.g. "Database Development").

     

     

    Questions:

    1.) Is it a mortal sin to NOT have the "PK" as the first column?

    2.) Is it a mortal sin to have the auto-increment "ID" be a "UK"?

    3.) Is it a mortal sin to have the "slug" be the "PK"?  (I could drop "ID", but I like have a number for each row as it makes it easier to visually find a given record.)

    4.) If the "slug" is the "PK", can it have spaces in it (e.g. "database development") or must I have the hyphen?

     

     

     

     

  17. @kicken

    Very nice, and thoughtful response!

     

    On 8/7/2020 at 3:16 AM, kicken said:

    > Am I expecting them to log in and then try to checkout again?

    Sure, why not?  That's exactly what someone who was already a member and needed to renew would be doing.

    Except, if you are already a Member, then you know you have an account, so it follows you would log in.

    One concern about creating an account for a new Member is that they might not know that I created an account for them in the background - and considering this is a one page checkout, it sorta implies it is an "all or none" process.

     

    On 8/7/2020 at 3:16 AM, kicken said:

    The user and order should already be tied together.  Then you tie the payment to the order and record whatever it's result is, be it failure or success.  If it's a failure, notify the user and offer them a chance to try another payment.  For the second payment you can either make a new order or just add a new payment record to the existing order.  Totally up to you and what you want to do.

    So are you implying that regardless of the other stuff we are discussing, that I should wrap the CREATE MEMBER and CREATE ORDER in a MySQL transaction?

     

     

    On 8/7/2020 at 3:16 AM, kicken said:

    Unless you're doing a one-time payment thing, which if I remember correctly from prior threads you're not, then that's work you have to do anyway for when a member's subscription expires.  When that happens they need to be able to login so they can manage their account (and potentially renew) but you'll want to block the content.  So...Do the work and take advantage of it for new sign-ups as well.

    Yes, you remember correctly.

    And, yes, I do have to build my renewal module to force them to log in before they can renew.

    I'm just not crazy from a UI/flow standpoint of surreptiously creating an account in the background when the creation of a Member is on the single-page checkout form, and if the payment fails, I think most people assume their account was not created.

    I guess I can re-direct them to an error page stating that their payment failed, and asking them to log in to try again...

    But since I also require the user to activate his/her account by clicking on a link in an email I send them, again, all of this is an awkward UI/flow.

    (If it was me signing up for the first time, I'd rather the checkout page was re-loaded, and I was forced to re-enter my password and credit card details.)

     

     

    On 8/7/2020 at 3:16 AM, kicken said:

    That is something you can and ideally should catch client-side with JS before the form is submitted.

    Unfortunately I don't know Javascript, so that is a version 2.0 thing.

     

     

    On 8/7/2020 at 3:16 AM, kicken said:

    > So if the payment fails, why can't I just re-display the checkout form with sticky data minus sensitive data like payment info?

     

    Sure, you can do that.  If you go for the simple roll everything back method then that's probably all you need to do.  If you create and keep the account however, consider this scenario:

    I'm trying to sign up for your site but, unexpectedly, my payment fails.   "Oh my, how can this be!" I say.  I open a new tab and rush over to my online banking to double check my account.  Upon logging in I discover my card has been deactivated due to potential fraud, some other site must have been hacked!  My new card is on the way in the mail. "Drats, I guess I will just have to wait til next week for the new card" I sigh.  Since signing up obviously won't happen today I shutdown the computer and go on with my day.  **inspired by a true story

    With the account having already been created as part of the original attempt, I can't just sign up again with the same details, an account already exists.  So I'd need a way to login then complete the checkout from there.

    Well, in that scenario, I think having not committed anything, and thus not created an account, would be easiest.  (Never having been a Member before, if you come back in 3 days, you are likely to have forgotten where you left off, and if you have to remember what your username/email that you used were, and remember your password - which is hopefully unique - then that could cause real confusion, right?

    I could email the potential Member an e-mail stating an account was created using username/email (blah), and state that the person will need to log in and try checking out again, but in the scenario that you described, that just seems messier to me than having things fail the first time with no account created, and then you come back when you come back and start fresh - with the ability to use the same username/email/password.

    (Of course, IF I had a sales/marketing/customer retention dept, I would get yelled at for not capturing the user's details and creating a crippled account, because that provides a way to harass people to finish signing up, but them I'm not that kind of person/business, i.e. all about the $$$...)

     

     

    On 8/7/2020 at 3:16 AM, kicken said:

    Ultimately, this entire thread just boils down to "Pick something, and do it"

    Yeah, just didn't want to choose a path that was destined to fail...

     

     

    On 8/7/2020 at 3:16 AM, kicken said:

    There's not a single right way to handle this.  There are some ways that can arguably be better than others, but every option has a pros and cons list and it's to you to decide how the pros weigh vs the cons.

    Option 1) Single transaction, roll it all back if the payment fails

    Pros: Easy to implement.  Fairly easy for users to understand (had a problem, nothing saved, start over)

    Cons: Less flexible.  Less data to track/analyze. If the user calls wondering why their payment failed, you may not be able to answer that question. Possibly annoying for users, particularly if the sign-up process is long. 

    Not having ever build an ecommerce site before, and never having used my merchant account, I cannot really comment on what I would want/need, BUT you make a good point there about being able to field emails/calls on, "Why did my payment fail?"

     

     

    On 8/7/2020 at 3:16 AM, kicken said:

    Option 2) Create account, handle payment separately.

    Pros: More flexible. Able to track all orders and payments.  Potentially able to answer user's questions about payment failures.

    Cons: Users may not expect the account to exist (easily addressed though). Harder to implement.

    That was/is my concern, although I mentioned how that can be addressed...

     

     

    On 8/7/2020 at 3:16 AM, kicken said:

    Option n) whatever other way you can think of.  Weigh it's pros and cons.

    Yeah.

     

     

    On 8/7/2020 at 3:16 AM, kicken said:

    Pick a route.  Figure out what process you want to follow and Just do it.™  If you're ok with not tracking every payment and want to take the easy route, then go for option 1.  Otherwise go for option 2 and figure out what features you need and what you can live without.  Have some other idea you think is good? Go for option n.  You can always revise the process later if it turns out to not work out as well as you'd hoped. 

    Yeah, I am just exhausted t=with this never ending site and am afraid I'm gonna make so fatal design decision on my ecommerce module and this will never get done.

    (This must be what it feels like when a Ph.D. student is starting their dissertation?!)  😕

     

     

    On 8/7/2020 at 3:16 AM, kicken said:

    If it were me, I'd lean toward option 2 and putting in the work necessary to track all the payment details.

    Do you prefer fixing the incomplete Order, or leaving it orphaned and creating a second one?

     

     

    On 8/7/2020 at 3:16 AM, kicken said:

    I'd probably make the sign-up a two-step process rather than a one-step process just to help make it clear to then user that the account is created regardless of payment status.  You can have both steps on the same page if you wanted for a nicer UX, just use JS to process the account creation via AJAX in the background.

    Yeah, a lack of Javascript is a weakness of mine, so that is out.

    And I am already scrapping the design I had last year which was a "traditional" sign up process like PHPFreaks uses.  But as mentioned, I research UX/UI, mobile, and  cart-abandonment AT LENGTH, and all of the research says, "Make checkout as quick and easy as possible, where you create the account and get the $$$ all in one step!"

    (I still want the user to verify their email using account activation, but I am delaying that until after I get their $$$.)

     

     

    On 8/7/2020 at 3:16 AM, kicken said:

    Most places I've dealt with in the last few years offer a trial period which kind of forces them into creating the account separate from payment handling.  Many don't even ask for payment details until the trial ends.  Some have asked up front but don't process them until after the trial.  A few have run a small authorization (which is later voided) just to check the validity of the card and prevent signup on failure; mainly to prevent abuse where people just sign up for a new trial every month and/or as a type of captcha to prevent bots from signing up.

    Good point, and yes, I do offer a "30-day Trial for $1" where I use the exact same checkout process as we have been discussing, but the only difference is I only charge $1.  (Would need to address my concerns above in the same way as a full annual subscription on a new user.)

     

    I appreciate you taking time to spell out different options and your reasoning!

     

    I guess ultimately I can program your Option #1 or Option #2, I just personally lean towards the easy (for the user) option.

    But I think I need to kick all of this around some.

     

    Fortunately tonight and tomorrow, I am setting up the tables and such in MySQL, so i don't need a decision just yet.  (And implementing either approach will NOT impact my database design which I have spent like the last year working on?!  And which *should* be bullet-proof...)

     

    In closing, if I had one wish before I die - "figure of speech", God - it would be to finish this damn website and to see it successfully "go live"

    This is worse than the worst tooth-ache ever....  :facewall:

     

     

     

  18. @requinix

     

    On 7/31/2020 at 10:53 AM, requinix said:

    Always record data. A payment failing counts as data.

    Why create an account if the payment fails?

     

    On 7/31/2020 at 10:53 AM, requinix said:

    I think part of my problem with this thread is that I don't even understand why you would consider this idea.

    Sign the user up. That's a separate step. Store the order data. That's a separate step. Take the payment information. That's a separate step. Attempt to process the payment information. That's a separate step. There is no reason why one of those steps resulting in a problem should somehow undo what happened before it.

    I wasn't proposing undoing anything, but rather not committing things.

     

     

     

    On 7/31/2020 at 10:53 AM, requinix said:

    Even if you went for step #2 (which, to reiterate, is wrong), there's nothing incomplete here. A user is a user. Doesn't matter whether they have an order, let alone have successfully paid for an order.

    So if I create the user account and the payment fails, then what comes next?

    Am I expecting them to log in and then try to checkout again?

     

    On 7/31/2020 at 10:53 AM, requinix said:

    These are two completely different concepts. And the order has every reason to exist even if the payment didn't go through - again, always record data.

    Similar questions to above...

    If I create an order and the payment fails, then what is the next step?

    In other words, how do I tie the user and the order to what failed?

     

    My setup is unique in that I am doing a one-stop checkout.  And I'm doing this because after reading lots of research from UI experts, they all say that to avoid "cart abandonment" you should make checkout as simple as possible!

    So a shopper adds a membership plan to their cart and clicks "Checkout".  They are presented with a form asking for a username, email, password, and payment details.

    If the payment succeeds, then the plan was to create the member account, create an order, and send them an email to activate their account.

    I was thinking this way because if you haven't paid then you can't do anything on my site (e.g. view paid content).

    I could make it so people can log in and maybe see the unfinished checkout, while restricting access to content, but that just seems like more work.

     

     

     

    On 7/31/2020 at 10:53 AM, requinix said:

    If the disadvantages outweigh the advantages when why would Amazon do it?

    Well, I don't know what Amazon does on their backend, but they probably have a skyscraper full of developers and I don't!

     

     

     

    On 7/31/2020 at 10:53 AM, requinix said:

    It's not your fault the payment failed. (Probably.) You have no control over that. If it fails and the user aborts the order then sucks to be them.

    If a payment failed it is likely they just didn't have enough available credit.  (Although maybe they fat-fingered their credit card number?)

     

    On 7/31/2020 at 10:53 AM, requinix said:

    You cannot store order data for an anonymous user. It's not good. The user must exist so that you can associate the order to it. That's fundamental ecommerce there.

    Agreed.

     

    On 7/31/2020 at 10:53 AM, requinix said:

    > and I'd have to build additional functionality to allow them to try to checkout again,

    That's right. You have to do that.

    So if the payment fails, why can't I just re-display the checkout form with sticky data minus sensitive data like payment info?

    It's pretty standard in PHP to just reload a form if their are data entry errors and let the user fix them.

    Can't I do that on my checkout form?

     

     

    On 7/31/2020 at 10:53 AM, requinix said:

    I'd say "you have to do that" but you already have: it's called "anonymous people don't have access to the paid portion of the site". The only difference now is that you need to tighten your restrictions to allow users who have also paid (or have an active subscription or whatever).

    So create the user account, create the order, tell them their payment failed, then what?

    Require this new semi-user to log in?

    (Do I force them to activate their account by verifying their email first?)

    After they log in, then what?

    Is their shopping cart still like before they checked out?

    Do I try and have them complete their existing, unfinished order?

    Do I have them try and checkout again, this time creating a *new* order?

    Do I send them to "My Account" and somewhere in their have them find their incomplete order?

     

    All of this may seem obvious to others, but I guess things I have purchased online have worked the first time, so I cannot recall a "recovery process" that I had to deal with.

    Furthermore, I think this is trickier to me since I am selling an account and not a physical product like socks!    

     

     

  19. @requinix,

    Thanks for the responses above, but if I wasn't clear earlier, what I am really trying to figure out is how to handle things if the payment fails.

    In that case, do I...

    1.) Go ahead and create the Member, Order, and Order_Details tables, then display a message to the user that their payment failed, and ask them to contact me?

    2.) Reload the checkout form leaving some fields "sticky" (e.g. username and email), but leaving others blank (e.g. password, payment details).  In this case I would *not* create any tables since I don't have their money yet.

     

    Maybe approach #2 isn't "customer focused", but I'm not crazy about creating *incomplete* Member, Order and Order_Details records.  That seems like it creates a mess on the back-end for me, as well as from a customer-service standpoint.  Maybe that is how Amazon.com might do things, but I'm thinking it has more down-sides than pluses.

    Or, if I don't take the #1 approach, am I a "jerk" and can I expect to lose sales when a payment fails?

     

    *******

    Fwiw, the reason I'm not crazy about approach #1 is that then that forces me to allow people to log into an account that they haven't paid for, and I'd have to build additional functionality to allow them to try to checkout again, and I'd have to put in extra logic to prohibit them from having access to the paid portion of the site and things like an online book they may have tried to purchase.

    As I see it, it would be like a move theater letting you inside when your credit card failed while buying a ticket, and then asking you to wait in the lobby until you found another way to pay them.  (Or maybe giving you a large bucket of buttery popcorn, but telling you not to eat any while they try your card again...)

    How well do you think that would work?

    And think of how much work that would create for the movie theater and staff for that exception?! 

    Just my 2-cents...

     

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.