l i n u x - u s e r s - g r o u p - o f - d a v i s
L U G O D
 
Next Meeting:
April 21: Google Glass
Next Installfest:
TBD
Latest News:
Mar. 18: Google Glass at LUGOD's April meeting
Page last updated:
2003 Apr 25 18:54

The following is an archive of a post made to our 'vox-tech mailing list' by one of its subscribers.

Report this post as spam:

(Enter your email address)
[vox-tech] Sales/Shipping/Payment without Transactions.
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[vox-tech] Sales/Shipping/Payment without Transactions.



On Fri, Apr 25, 2003 at 09:26:44AM -0700, Micah J. Cowan wrote:
> Answer the question: what would you do to avoid having either an
> order in the DB marked to be shipped without payment, or payment for
> an order which didn't get into your DB?

  I will attempt to give an example, I'm not saying this is the only 
way to do this without transactions.  This may be a bit long winded.

  Point out problems if you see them.

    Later,
      Mike Simons

  To keep this "simple", no transactions, no locking, many definitions,
I focus on only the shipping part of the question and simply payment 
types to only credit cards.

  The samples below target mysql, and have a perl-ish syntax to them... 
but syntax is not correct.

  There is one requirement: when an insert or update statement is run it 
*must* hit the disk before any other insert or update statements that
come after it.  Said another way, if the database stores things out of
order you have a problem.


table definitions:
===
CREATE TABLE Customers (
  c_id INT UNSIGNED NOT NULL AUTO_INCREMENT,   # customer id
  c_first VARCHAR(20) NOT NULL,                # customer first name
  c_last VARCHAR(20) NOT NULL                  # customer last name
);

CREATE TABLE Items (
  i_id INT UNSIGNED NOT NULL AUTO_INCREMENT,   # item id
  i_name VARCHAR(20) NOT NULL,                 # item name
  i_price DOUBLE(16,2) NOT NULL                # item price
);

CREATE TABLE Orders (
  o_id INT UNSIGNED NOT NULL AUTO_INCREMENT,   # order id
  o_time TIMESTAMP NOT NULL,                   # timestamp
  o_c_id INT UNSIGNED NOT NULL,                # customer id
  o_p_id INT UNSIGNED NULL                     # payment id
  p_status ENUM ('creating', 'rejected', 'pending', 'shipped')
                                               # order processing status
);

CREATE TABLE OrderedItems (
  oi_o_id INT UNSIGNED NOT NULL,               # order id
  oi_i_id INT UNSIGNED NOT NULL,               # item id
  oi_count INT UNSIGNED NOT NULL,              # how many items
);

CREATE TABLE Payments (
  p_id INT UNSIGNED NOT NULL AUTO_INCREMENT,   # payment id
  p_time TIMESTAMP NOT NULL,                   # timestamp
  p_ccn INT(16) NOT NULL,                      # credit card number
  p_expr INT(4) NOT NULL,                      # expiration date
  p_auth INT(6) NULL                           # authorization number
  p_status ENUM ('checking', 'rejected', 'approved', 'done') # approval status
);
===


sample transaction:
===
# Get the Customer ID...
  select c_id from Customers where (c_first = 'John' && c_last = 'Doe');
  $customer_id = $c_id
or...
  insert into Customers values (NULL, 'John', 'Doe');
  $customer_id = $mysql_insertid


# Start an order
insert into Orders values (NULL, NULL, c_id, NULL, 'creating');
$order_id = $mysql_insertid


# Add items into ordered Items
for each item in the list to order
  insert into OrderedItems values ($order_id, $item_type, $how_many);


# Start payment authorization
insert into Payments values 
  (NULL, NULL, $card_number, $card_expire, NULL, 'checking');
$payment_id = $mysql_insertid

($auth_number, #status) = get_credit_auth();  # payment authorization
# status is either 'rejected' or 'approved'

#### race condition (1)

# Finish payment authorization
update Payments set p_auth=$auth_number, p_status=$status
  where (p_id = $payment_id);

#### race condition (2)

# Finish order
if ($status not equal 'approved') {
  update Orders set p_id=$payment_id,o_status='rejected'
    where (o_id = $order_id);

  print "your payment has been rejected";
} else {
  update Orders set p_id=$payment_id,o_status='pending'
    where (o_id = $order_id);

  print "your authorization number is $auth_number";
}
===


logic:
===
to find orders that are being entered right now:
  select o_id from Orders where o_status = 'creating';

to find orders that were rejected for credit reasons:
  select o_id from Orders where o_status = 'rejected';

to find orders that need product shipped:
  select o_id from Orders where o_status = 'pending';


to find which payments need to be billed to the credit card company:
  select p_id from Payments where p_status = 'approved';


to find out which transactions are "stale" or left over from
  an application crash, look for order status 'creating' or 
  payment status 'checking'... at a time when the system is 
  offline or after the time stamp is X days old.
===


Race Condition 1:
====
  Credit card company issued a authorization, but the authorization
number is not stored.

  Since the payment status never changes to 'approved', order status
never moves to 'pending', and items are not shipped.
  Even though a credit card authorization is issued I expect the customer
is not billed.  I have not dealt with credit card transactions before, but 
here is why I suspect that this is fine.

  In my mind the authorization step just puts a short term hold on the
funds, the item_selling_company must *bill* the credit card company
with authorization number to actually collect money.  This is why some
transactions clear on one's credit card statement days away from when
the store receipt says.
====

Race Condition 2:
====
  Credit authorization is stored, but customer is not informed that
the transaction is complete.
  
  The items will ship to the customer.  The only concern is they may
redo the transaction and order a second shipment... this is out of
scope.

  The application could make it easy for the user to check the status 
of they most recent order.
====

  I think a system like the one above would be very easy to track down
problems in, at each important step a log is made.

  Instead of using status ENUM columns like I did above or if you were
working with a DB that doesn't support them.  You can do null-able 
timestamps one for each order state:
  Created
  Approved
  Shipped
and for each payment state:
  Requested
  Approved      (set to a not-null known value for a reject)
  Billed

-- 
GPG key: http://simons-clan.com/~msimons/gpg/msimons.asc
Fingerprint: 524D A726 77CB 62C9 4D56  8109 E10C 249F B7FA ACBE

Attachment: pgp00018.pgp
Description: PGP signature



LinkedIn
LUGOD Group on LinkedIn
Sign up for LUGOD event announcements
Your email address:
facebook
LUGOD Group on Facebook
'Like' LUGOD on Facebook:

Hosting provided by:
Sunset Systems
Sunset Systems offers preconfigured Linux systems, remote system administration and custom software development.

LUGOD: Linux Users' Group of Davis
PO Box 2082, Davis, CA 95617
Contact Us

LUGOD is a 501(c)7 non-profit organization
based in Davis, California
and serving the Sacramento area.
"Linux" is a trademark of Linus Torvalds.

Sponsored in part by:
EDGE Tech Corp.
For donating some give-aways for our meetings.