I l @ ve RuBoard |
Now that you have addresses, you can write the wallet. A lot of the credit card code (see Listing 11.11) is very similar to the address code. I'm just going to discuss the areas that are different. Listing 11.11 CreditCard.javapackage com.bfg.customer; import java.util.Vector; import java.util.HashMap; import java.util.Iterator; import org.apache.turbine.services.db.TurbineDB; import org.apache.turbine.util.db.pool.DBConnection; import org.apache.turbine.util.TurbineConfig; import com.bfg.exceptions.CustomerActivityException; import java.sql.*; import java.util.ResourceBundle; import org.apache.log4j.Category; import javax.naming.NamingException; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingEnumeration; import javax.naming.directory.InitialDirContext; import java.text.SimpleDateFormat; import java.text.ParseException; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.Date; public class CreditCard { static Category cat = Category.getInstance(CreditCard.class); private static ResourceBundle sql_bundle = ResourceBundle.getBundle("com.bfg.customer.SQLQueries"); protected int cardID; protected Address address; protected Customer customer; protected String cardOwner; protected String cardType; protected String cardNumber; protected int expMonth; protected int expYear; public int getCardID() { return cardID; } public void setCardID(int id) { cardID = id; } public Address getAddress() { return address; } public void setAddress(Address addr) { address = addr; } public Customer getCustomer() { return customer; } public void setCustomer(Customer cust) { customer = cust; } public String getCardOwner() { return cardOwner; } public void setCardOwner(String name) { cardOwner = name; } public String getCardType() { return cardType; } public void setCardType(String name) { cardType = name; } public String getCardNumber () { return cardNumber; } public String getObscuredNumber () { if ((cardNumber != null) && (cardNumber.length() > 6)) { String digitsOnly = getDigitsOnly(cardNumber); String allStars = "****************************************"; return digitsOnly.substring(0,2) + allStars.substring(0, digitsOnly.length() - 6) + digitsOnly.substring(digitsOnly.length() - 4, digitsOnly.length()); } else { return ""; } } public void setCardNumber(String name) { cardNumber = name; } public int getExpMonth () { return expMonth; } public void setExpMonth(int month) { expMonth = month; } public int getExpYear () { return expYear; } public void setExpYear(int year) { expYear = year; } private HashMap validationErrors = new HashMap(); public String getFieldError(String fieldname) { return((String)validationErrors.get(fieldname)); } public void addFieldError(String fieldname, String error) { validationErrors.put(fieldname, error); } public boolean validateCCDate(int expMonth, int expYear) { SimpleDateFormat formatter = new SimpleDateFormat ("MM/yy"); try { GregorianCalendar c = new GregorianCalendar(); c.setTime(formatter.parse(expMonth + "/" + expYear)); c.roll(Calendar.MONTH, 1); c.set(Calendar.DATE, 1); c.set(Calendar.HOUR, 0); c.set(Calendar.MINUTE, 0); c.set(Calendar.SECOND, 0); Date now = new Date(); return (now.compareTo(c.getTime()) < 0); } catch (ParseException ex) {} ; return false; } private String getDigitsOnly (String s) { StringBuffer digitsOnly = new StringBuffer (); char c; for (int i = 0; i < s.length (); i++) { c = s.charAt (i); if (Character.isDigit (c)) { digitsOnly.append (c); } } return digitsOnly.toString (); } public boolean validateCreditCardNumber (String cardNumber) { String digitsOnly = getDigitsOnly (cardNumber); int sum = 0; int digit = 0; int addend = 0; boolean timesTwo = false; int digitLength = digitsOnly.length(); boolean foundcard = false; // MC if (digitsOnly.startsWith("51") digitsOnly.startsWith("52") digitsOnly.startsWith("53") digitsOnly.startsWith("54")) { if (digitLength != 16) return false; foundcard = true; } // VISA if (digitsOnly.startsWith("4")) { if ((digitLength != 16) && (digitLength != 13)) return false; foundcard = true; } // AMEX if (digitsOnly.startsWith("34") digitsOnly.startsWith("37")) { if (digitLength != 15) return false; foundcard = true; } // DISC if (digitsOnly.startsWith("6011")) { if (digitLength != 16) return false; foundcard = true; } if (!foundcard) return false; for (int i = digitsOnly.length () - 1; i >= 0; i) { digit = Integer.parseInt (digitsOnly.substring (i, i + 1)); if (timesTwo) { addend = digit * 2; if (addend > 9) { addend -= 9; } } else { addend = digit; } sum += addend; timesTwo = !timesTwo; } int modulus = sum % 10; return modulus == 0; } public boolean validateCreditCard() { validationErrors.clear(); boolean valid = true; if ((cardType == null) (cardType.length() == 0)) { addFieldError("cardType", "Card Type is required."); valid = false; } if ((cardOwner == null) (cardOwner.length() == 0)) { addFieldError("cardOwner", "Cardholder Name is required."); valid = false; } if ((cardNumber == null) (cardNumber.length() == 0)) { addFieldError("cardNumber", "Card Number is required."); valid = false; } else { if (!validateCreditCardNumber(cardNumber)) { addFieldError("cardNumber", "Invalid Card Number"); valid = false; } } if (expMonth == 0) { addFieldError("expMonth", "Expiration Month is required."); valid = false; } if (expYear == 0) { addFieldError("expYear", "Expiration Year is required."); valid = false; } if (!validateCCDate(expMonth, expYear)) { addFieldError("expYear", "Expired Card Date"); valid = false; } return valid; } public static CreditCard findCreditCard(int cardID) throws CustomerActivityException { CreditCard cc = null; DBConnection dbConn = null; try { dbConn = TurbineDB.getConnection(); if (dbConn == null) { cat.error("Can't get database connection"); throw new CustomerActivityException(); } PreparedStatement pstmt = dbConn.prepareStatement(sql_bundle.getString("creditQuery")); pstmt.setInt(1, cardID); ResultSet rs = pstmt.executeQuery(); if (rs.next()) { cc = new CreditCard(); cc.setCardType(rs.getString("CARD_TYPE")); cc.setCardNumber(rs.getString("CARD_NUMBER")); cc.setCardOwner(rs.getString("CARD_OWNERNAME")); cc.setExpMonth(rs.getInt("CARD_EXPMONTH")); cc.setExpYear(rs.getInt("CARD_EXPYEAR")); cc.setAddress(Address.findAddress(rs.getInt("ADDRESS_KEY"))); cc.setCustomer(Customer.findCustomer(rs.getString("EMAIL_ADDRESS"))); } else { cat.error("Couldn't find record for Credit Card"); } rs.close(); pstmt.close(); } catch (Exception e) { cat.error("Error during findCreditCard", e); throw new CustomerActivityException(); } finally { try { TurbineDB.releaseConnection(dbConn); } catch (Exception e) { cat.error("Error during release connection", e); } } return cc; } public void createCreditCard() throws CustomerActivityException { DBConnection dbConn = null; try { dbConn = TurbineDB.getConnection(); if (dbConn == null) { cat.error("Can't get database connection"); throw new CustomerActivityException(); } PreparedStatement pstmt = dbConn.prepareStatement(sql_bundle.getString("creditInsert")); pstmt.setInt(1, getCustomer().getCustomerId()); pstmt.setString(2, getCardType()); pstmt.setString(3, getCardNumber()); pstmt.setString(4, getCardOwner()); pstmt.setInt(5, getExpMonth()); pstmt.setInt(6, getExpYear()); pstmt.setInt(7, getAddress().getAddressID()); pstmt.executeUpdate(); pstmt.close(); pstmt = dbConn.prepareStatement(sql_bundle.getString("addressID")); ResultSet rs = pstmt.executeQuery(); if (rs.next()) { setCardID(rs.getInt(1)); } else { cat.error("Couldn't find record for new Credit Card"); } rs.close(); pstmt.close(); } catch (Exception e) { cat.error("Error during createCreditCard", e); throw new CustomerActivityException(); } finally { try { TurbineDB.releaseConnection(dbConn); } catch (Exception e) { cat.error("Error during release connection", e); } } } public void updateCreditCard() throws CustomerActivityException { DBConnection dbConn = null; try { dbConn = TurbineDB.getConnection(); if (dbConn == null) { cat.error("Can't get database connection"); throw new CustomerActivityException(); } PreparedStatement pstmt = dbConn.prepareStatement(sql_bundle.getString("cardUpdate")); pstmt.setInt(1, getCustomer().getCustomerId()); pstmt.setString(2, getCardType()); pstmt.setString(3, getCardNumber()); pstmt.setString(4, getCardOwner()); pstmt.setInt(5, getExpMonth()); pstmt.setInt(6, getExpYear()); pstmt.setInt(7, getAddress().getAddressID()); pstmt.setInt(8, getCardID()); pstmt.executeUpdate(); pstmt.close(); } catch (Exception e) { cat.error("Error during updateCreditCard", e); throw new CustomerActivityException(); } finally { try { TurbineDB.releaseConnection(dbConn); } catch (Exception e) { cat.error("Error during release connection", e); } } } public void deleteCreditCard() throws CustomerActivityException { DBConnection dbConn = null; try { dbConn = TurbineDB.getConnection(); if (dbConn == null) { cat.error("Can't get database connection"); throw new CustomerActivityException(); } getAddress().deleteAddress(); PreparedStatement pstmt = dbConn.prepareStatement(sql_bundle.getString("credit Delete")); pstmt.setInt(1, getCardID()); pstmt.executeUpdate(); pstmt.close(); } catch (Exception e) { cat.error("Error during deleteCreditCard", e); throw new CustomerActivityException(); } finally { try { TurbineDB.releaseConnection(dbConn); } catch (Exception e) { cat.error("Error during release connection", e); } } } } The getObscuredNumber method is used to return the credit card number with everything but the first two and last four digits obscured. At last, you have some interesting validations! First off, the code must make sure that it hasn't been handed an expired credit card. Working with dates in Java can seem a bit annoying at times because each of the various ways of representing time ( Time , Date , and Calendar ) has certain operations that it supports and certain ones that it doesn't. You might need to convert back and forth several times to get the final result. This code is one of several ways to find out whether a card is expired. It sets the calendar to the first second of the following month and checks whether today's date is greater than it ”a card that expires on 10/02 really expires at midnight of 10/31/02. If you wanted to internationalize the application, you would use resource bundles instead of the hard-wired error strings here so that locale-dependent messages can be returned. You will also want to prevalidate the card number. This code will do a number of checks:
Because an ADDRESS record is being used to store the billing address for the credit card, the code needs to read in the Address object when it gets a credit card from the database. Because the class needs the Address ID when it creates a new credit card, you'll need to have the code create the Address object first, set the credit card's Address field to it, and then call createCreditCard . Adding Wallet Support to CustomerNow add the wallet support to Customer.java (see Listing 11.12), immediately following the code that you added to read in the address book in findCustomer . It's interesting to note that two different approaches were taken with addresses and credit cards. Because addresses are pointed to by both customers and credit cards, an xref table was used to associate them with customers. Because credit cards belong directly to customers, you could "hard-wire" the customer ID directly in the credit card record. That way Customer.java doesn't need a function to add and remove credit cards. Listing 11.12 More Changes to Customer.javapstmt = dbConn.prepareStatement(sql_bundle.getString("getWallet")); pstmt.setInt(1, cust.getCustomerId()); rs = pstmt.executeQuery(); while (rs.next()) { CreditCard cc = CreditCard.findCreditCard(rs.getInt(1)); cust.wallet.put(new Integer(cc.getCardID()), cc); } rs.close(); pstmt.close(); Now it's time to write the JSP. The MyAccount code (in Listing 11.13) is a clone of the code for the address book (in Listing 11.9). Listing 11.13 Adding Credit Cards to MyAccount.jsp<center><h2>Credit Cards</h2></center> <A HREF="NewCreditCard.jsp">Add New Card</A> <% if (customer.getWallet().size() > 0) { %> <TABLE WIDTH="100%"> <TR><TH>Cardholder</TH><TH>Card Number</TH><TH>Edit</TH><TH>Delete</TH></TR> <% Iterator it = customer.getWallet().keySet().iterator(); while (it.hasNext()) { CreditCard cc = (CreditCard) customer.getWallet().get(it.next()); %> <TR><TD><%= cc.getCardOwner() %> </TD> <TD><%= cc.getObscuredNumber() %></TD> <TD><A HREF="NewCreditCard.jsp?operation=update&cardId=<%= cc.getCardID() %>">X</ A></TD> <TD><A HREF="DeleteAddress.jsp?cardId=<%= cc.getCardID() %>" TARGET="tempwindow">X</A></TD></TR> <% } %> </TABLE> <% } %> The NewCreditCard.jsp (in Listing 11.14) code needs all the code that NewAddress.jsp had (because it needs to record the address), plus additional code to handle the credit card “specific data. It would be nice if you could somehow include the address code rather than duplicate it, but it is not worth the effort in this case ”there's only a single reuse of the code, and making the address code general would involve some heavy rewrites. Listing 11.14 NewCreditCard.jsp<%@ include file="/jsp/cust/AutoLogin.jsp" %> <%@ page import="com.bfg.customer.Customer" %> <%@ page import="com.bfg.customer.Address" %> <%@ page import="com.bfg.customer.CreditCard" %> <%@ page import="java.text.NumberFormat" %> <jsp:useBean id="customer" class="com.bfg.customer.Customer" scope="session"/> <jsp:useBean id="newaddr" class="com.bfg.customer.Address" scope="request"/> <jsp:useBean id="newcredit" class="com.bfg.customer.CreditCard" scope="request"/> <% if (customer.getEmail() == null) { response.sendRedirect("Login.jsp"); return; } %> <jsp:setProperty name="newaddr" property="*"/> <jsp:setProperty name="newcredit" property="*"/> <% NumberFormat nf = NumberFormat.getInstance(); String operation = request.getParameter("operation"); if (operation == null) { operation = "create"; } String cardId = request.getParameter("cardId"); Integer id = null; if (!operation.equals("update")) { newcredit.setCardNumber(request.getParameter("newCardNumber")); } else { if ((cardId != null) && (cardId.length() > 0)) { try { Number num = nf.parse(request.getParameter("cardId")); id = new Integer(num.intValue()); } catch (Exception e) { response.sendRedirect("general_error.jsp"); return; } } CreditCard c = (CreditCard) customer.getWallet().get(id); if (c.getObscuredNumber().equals(request.getParameter("newCardNumber"))) { newcredit.setCardNumber(c.getCardNumber()); } else { newcredit.setCardNumber(request.getParameter("newCardNumber")); } } if (request.getParameter("SUBMITTED") != null) { if (newcredit.validateCreditCard() && newaddr.validateAddress()) { if (operation.equals("update")) { CreditCard cc = (CreditCard) customer.getWallet().get(id); if (cc != null) { newcredit.setCustomer(customer); newcredit.setCardID(id.intValue()); newcredit.setAddress(newaddr); newcredit.setCustomer(customer); newaddr.setAddressID(cc.getAddress().getAddressID()); newaddr.updateAddress(); newcredit.updateCreditCard(); customer.getWallet().put(id, newcredit); response.sendRedirect("MyAccount.jsp"); return; } else { response.sendRedirect("noaccess.jsp"); return; } } else { newaddr.createAddress(); newcredit.setAddress(newaddr); newcredit.setCustomer(customer); newcredit.createCreditCard(); customer.getWallet().put(new Integer(newcredit.getCardID()), newcredit); response.sendRedirect("MyAccount.jsp"); return; } } } else { if (operation.equals("update")) { CreditCard card = (CreditCard) customer.getWallet().get(id); if (card != null) { newcredit = card; newaddr = card.getAddress(); } } } if (newaddr.getLastName() == null) { newaddr.setLastName(""); } if (newaddr.getFirstName() == null) { newaddr.setFirstName(""); } if (newaddr.getStreet1() == null) { newaddr.setStreet1(""); } if (newaddr.getStreet2() == null) { newaddr.setStreet2(""); } if (newaddr.getCity() == null) { newaddr.setCity(""); } if (newaddr.getState() == null) { newaddr.setState(""); } if (newaddr.getPostalCode() == null) { newaddr.setPostalCode(""); } if (newcredit.getCardOwner() == null) { newcredit.setCardOwner(""); } if (newcredit.getCardType() == null) { newcredit.setCardType(""); } if (newcredit.getCardNumber() == null) { newcredit.setCardNumber(""); } %> <% if (operation.equals("update")) { %> <HEAD><TITLE>Edit Credit Card</TITLE></HEAD><BODY> <% } else {%> <HEAD><TITLE>Create Credit Card</TITLE></HEAD><BODY> <% } %> <%@ include file="/jsp/includes/bfgheader.jsp" %> <% if (operation.equals("update")) { %> <CENTER><H1>Edit Credit Card</H1></CENTER> <% } else {%> <CENTER><H1>Create New Credit Card</H1></CENTER> <% } %> <FORM METHOD=POST ACTION="NewCreditCard.jsp"> <INPUT TYPE="HIDDEN" NAME="SUBMITTED" VALUE="T"> <INPUT TYPE="HIDDEN" NAME="operation" VALUE="<%= operation %>"> <INPUT TYPE="HIDDEN" NAME="cardId" VALUE="<%= request.getParameter("cardId") %>"> <% if (newcredit.getFieldError("cardOwner") != null) { %> <FONT COLOR="#FF0000"><%= newcredit.getFieldError("cardOwner")%></FONT><BR> <% } %> Name on Card: <INPUT NAME="cardOwner" TYPE="TEXT" SIZE=50 VALUE="<%= newcredit.getCardOwner() %>"><BR> <% if (newcredit.getFieldError("cardType") != null) { %> <FONT COLOR="#FF0000"><%= newcredit.getFieldError("cardType")%></FONT><BR> <% } %> Card Type: <SELECT NAME="cardType"> <OPTION VALUE="">-SELECT- <OPTION VALUE="VISA" <%= (newcredit.getCardType().equals("VISA"))?" SELECTED":"" %>>Visa <OPTION VALUE="MC" <%= (newcredit.getCardType().equals("MC"))?" SELECTED":"" %>>MasterCard <OPTION VALUE="AMEX" <%= (newcredit.getCardType().equals("AMEX"))?" SELECTED":"" %>>American Express <OPTION VALUE="DISC" <%= (newcredit.getCardType().equals("DISC"))?" SELECTED":"" %>>Discover </SELECT><BR> <% if (newcredit.getFieldError("cardNumber") != null) { %> <FONT COLOR="#FF0000"><%= newcredit.getFieldError("cardNumber")%></FONT><BR> <% } %> <% if (newcredit.getFieldError("expMonth") != null) { %> <FONT COLOR="#FF0000"><%= newcredit.getFieldError("expMonth")%></FONT><BR> <% } %> <% if (newcredit.getFieldError("expYear") != null) { %> <FONT COLOR="#FF0000"><%= newcredit.getFieldError("expYear")%></FONT><BR> <% } int expMonth = newcredit.getExpMonth(); int expYear = newcredit.getExpYear(); %> Card Number: <INPUT NAME="newCardNumber" TYPE="TEXT" SIZE=25 VALUE="<%= operation.equals("update")?newcredit.getObscuredNumber():newcredit.getCardNumber() %>"><BR> Expires: <SELECT NAME="expMonth"> <OPTION VALUE="">SELECT <OPTION VALUE="1" <%= (expMonth == 1)?" SELECTED":"" %>>Jan <OPTION VALUE="2" <%= (expMonth == 2)?" SELECTED":"" %>>Feb <OPTION VALUE="3" <%= (expMonth == 3)?" SELECTED":"" %>>Mar <OPTION VALUE="4" <%= (expMonth == 4)?" SELECTED":"" %>>Apr <OPTION VALUE="5" <%= (expMonth == 5)?" SELECTED":"" %>>May <OPTION VALUE="6" <%= (expMonth == 6)?" SELECTED":"" %>>Jun <OPTION VALUE="7" <%= (expMonth == 7)?" SELECTED":"" %>>Jul <OPTION VALUE="8" <%= (expMonth == 8)?" SELECTED":"" %>>Aug <OPTION VALUE="9" <%= (expMonth == 9)?" SELECTED":"" %>>Sep <OPTION VALUE="10" <%= (expMonth == 10)?" SELECTED":"" %>>Oct <OPTION VALUE="11" <%= (expMonth == 11)?" SELECTED":"" %>>Nov <OPTION VALUE="12" <%= (expMonth == 12)?" SELECTED":"" %>>Dec </SELECT> / <SELECT NAME="expYear"> <OPTION VALUE="">SELECT <OPTION VALUE="2002" <%= (expYear == 2002)?" SELECTED":"" %>>02 <OPTION VALUE="2003" <%= (expYear == 2003)?" SELECTED":"" %>>03 <OPTION VALUE="2004" <%= (expYear == 2004)?" SELECTED":"" %>>04 <OPTION VALUE="2005" <%= (expYear == 2005)?" SELECTED":"" %>>05 <OPTION VALUE="2006" <%= (expYear == 2006)?" SELECTED":"" %>>06 <OPTION VALUE="2007" <%= (expYear == 2007)?" SELECTED":"" %>>07 <OPTION VALUE="2008" <%= (expYear == 2008)?" SELECTED":"" %>>08 <OPTION VALUE="2009" <%= (expYear == 2009)?" SELECTED":"" %>>09 <OPTION VALUE="2010" <%= (expYear == 2010)?" SELECTED":"" %>>10 <OPTION VALUE="2011" <%= (expYear == 2011)?" SELECTED":"" %>>11 <OPTION VALUE="2012" <%= (expYear == 2012)?" SELECTED":"" %>>12<P> </SELECT> <H2>Billing Address</H2><P> <% if (newaddr.getFieldError("firstName") != null) { %> <FONT COLOR="#FF0000"><%= newaddr.getFieldError("firstName")%></FONT><BR> <% } %> <% if (newaddr.getFieldError("lastName") != null) { %> <FONT COLOR="#FF0000"><%= newaddr.getFieldError("lastName")%></FONT><BR> <% } %> First Name: <INPUT NAME="firstName" TYPE="TEXT" SIZE=30 VALUE="<%= newaddr.getFirstName() %>"> Last Name: <INPUT NAME="lastName" TYPE="TEXT" SIZE=40 VALUE="<%= newaddr.getLastName() %>"><BR> <% if (newaddr.getFieldError("street1") != null) { %> <FONT COLOR="#FF0000"><%= newaddr.getFieldError("street1")%></FONT><BR> <% } %> Street Addr 1: <INPUT NAME="street1" TYPE="TEXT" SIZE=80 VALUE="<%= newaddr.getStreet1() %>"><BR> Street Addr 2: <INPUT NAME="street2" TYPE="TEXT" SIZE=80 VALUE="<%= newaddr.getStreet2() %>"><BR> <% if (newaddr.getFieldError("city") != null) { %> <FONT COLOR="#FF0000"><%= newaddr.getFieldError("city")%></FONT><BR> <% } %> <% if (newaddr.getFieldError("state") != null) { %> <FONT COLOR="#FF0000"><%= newaddr.getFieldError("state")%></FONT><BR> <% } %> <% if (newaddr.getFieldError("postalCode") != null) { %> <FONT COLOR="#FF0000"><%= newaddr.getFieldError("postalCode")%></FONT><BR> <% } %> City: <INPUT NAME="city" TYPE="TEXT" SIZE=50 VALUE="<%= newaddr.getCity() %>"> State: <INPUT NAME="state" TYPE="TEXT" SIZE=2 VALUE="<%= newaddr.getState() %>"> Postal Code: <INPUT NAME="postalCode" TYPE="TEXT" SIZE=10 VALUE="<%= newaddr.getPostalCode() %>"><BR> <INPUT TYPE=SUBMIT> </FORM> <%@ include file="/jsp/includes/bfgfooter.jsp" %> You'll be using some tricky code with the credit card number to avoid displaying it to the customer. This is so that if someone gains unauthorized access to the account, they can't view credit card numbers that already have been entered. Because of this, don't use setProperties to set the credit card from the form submit; hand it in as a property called newCardNumber . If the page is not doing an update, the credit card number must be new. So, just set the bean's copy to the copy from the form submit. If the page is doing an update, it checks to see if the submitted card number is equal to the "obscured" version. If it is, the page gets the real card number and puts it in the newcredit bean. Again, the code to update the database is basically the same as the code for addresses, but now it needs to write out both a new or modified address record and a new or modified credit card record. If you have pull-down menus in your forms and you want them to come up with the previous value, you have to end up writing some cumbersome code to put a SELECTED tag on the appropriate value. I'm a fan of the boolean?trueval:falseval Java shorthand for these kinds of things. You need to have the code hide the card information when it displays it in the form. If the page is in an update, it places the obscured version of the card number in the form. If the customer doesn't change it, the code will match it up when the customer submits it and will replace it with the real credit card number before writing it to the database. If the customer does change it, the page will take the value entered. Figure 11.4. NewCreditCard.jsp
With credit card support in place, you can now create and edit your wallet to your heart's content.
Figure 11.5. MyAccount.jsp with credit cards.
|
I l @ ve RuBoard |