diff options
Diffstat (limited to 'trunk/etherpad/src/etherpad/store')
-rw-r--r-- | trunk/etherpad/src/etherpad/store/checkout.js | 300 | ||||
-rw-r--r-- | trunk/etherpad/src/etherpad/store/eepnet_checkout.js | 101 | ||||
-rw-r--r-- | trunk/etherpad/src/etherpad/store/eepnet_trial.js | 241 |
3 files changed, 642 insertions, 0 deletions
diff --git a/trunk/etherpad/src/etherpad/store/checkout.js b/trunk/etherpad/src/etherpad/store/checkout.js new file mode 100644 index 0000000..2a4d7e7 --- /dev/null +++ b/trunk/etherpad/src/etherpad/store/checkout.js @@ -0,0 +1,300 @@ +/** + * Copyright 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS-IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import("dateutils"); +import("email.sendEmail"); +import("jsutils.*"); +import("sqlbase.sqlobj"); +import("stringutils"); +import("sync"); + +import("etherpad.globals"); +import("etherpad.globals.*"); +import("etherpad.licensing"); +import("etherpad.utils.*"); + +import("static.js.billing_shared.{billing=>billingJS}"); + +function dollars(x, nocommas) { + if (! x) { return "0.00"; } + var s = String(x); + var dollars = s.split('.')[0]; + var pennies = s.split('.')[1]; + + if (!dollars) { + dollars = "0"; + } + + if (!nocommas && dollars.length > 3) { + var newDollars = []; + newDollars.push(dollars[dollars.length-1]); + + for (var i = 1; i < dollars.length; ++i) { + if (i % 3 == 0) { + newDollars.push(","); + } + newDollars.push(dollars[dollars.length-1-i]); + } + dollars = newDollars.reverse().join(''); + } + + if (!pennies) { + pennies = "00"; + } + + if (pennies.length == 1) { + pennies = pennies + "0"; + } + + if (pennies.length > 2) { + pennies = pennies.substr(0,2); + } + + return [dollars,pennies].join('.'); +} + +function obfuscateCC(x) { + if (x.length == 16 || x.length == 15) { + return stringutils.repeat("X", x.length-4) + x.substr(-4); + } else { + return x; + } +} + + +// validation functions + +function isOnlyDigits(s) { + return /^[0-9]+$/.test(s); +} + +function isOnlyLettersAndSpaces(s) { + return /^[a-zA-Z ]+$/.test(s); +} + +function isLength(s, minLen, maxLen) { + if (maxLen === undefined) { + return (typeof(s) == 'string' && s.length == minLen); + } else { + return (typeof(s) == 'string' && s.length >= minLen && s.length <= maxLen); + } +} + +function errorMissing(validationError, name, description) { + validationError(name, "Please enter a "+description+"."); +} + +function errorTooSomething(validationError, name, description, max, tooWhat, betterAdjective) { + validationError(name, "Your "+description+" is too " + tooWhat + "; please provide a "+description+ + " that is "+max+" characters or "+betterAdjective); +} + +function validateString(validationError, s, name, description, mustExist, maxLength, minLength) { + if (mustExist && ! s) { + errorMissing(validationError, name, description); + } + if (s && s.length > maxLength) { + errorTooSomething(validationError, name, description, maxLength, "long", "shorter"); + } + if (minLength > 0 && s.length < minLength) { + errorTooSomething(validationError, name, description, minLength, "short", "longer"); + } +} + +function validateZip(validationError, s) { + if (! s) { + errorMissing(validationError, 'billingZipCode', "ZIP code"); + } + if (! (/^\d{5}(-\d{4})?$/.test(s))) { + validationError('billingZipCode', "Please enter a valid ZIP code"); + } +} + +function validateBillingCart(validationError, cart) { + var p = cart; + + if (! isOnlyLettersAndSpaces(p.billingFirstName)) { + validationError("billingFirstName", "Name fields may only contain alphanumeric characters."); + } + + if (! isOnlyLettersAndSpaces(p.billingLastName)) { + validationError("billingLastName", "Name fields may only contain alphanumeric characters."); + } + + var validPurchaseTypes = arrayToSet(['creditcard', 'invoice', 'paypal']); + if (! p.billingPurchaseType in validPurchaseTypes) { + validationError("billingPurchaseType", "Please select a valid purchase type.") + } + + switch (p.billingPurchaseType) { + case 'creditcard': + if (! billingJS.validateCcNumber(p.billingCCNumber)) { + validationError("billingCCNumber", "Your card number doesn't appear to be valid."); + } + if (! isOnlyDigits(p.billingExpirationMonth) || + ! isLength(p.billingExpirationMonth, 1, 2)) { + validationError("billingMeta", "Invalid expiration month."); + } + if (! isOnlyDigits(p.billingExpirationYear) || + ! isLength(p.billingExpirationYear, 1, 2)) { + validationError("billingMeta", "Invalid expiration year."); + } + if (Number("20"+p.billingExpirationYear) <= (new Date()).getFullYear() && + Number(p.billingExpirationMonth) < (new Date()).getMonth()+1) { + validationError("billingMeta", "Invalid expiration date."); + } + var ccType = billingJS.getCcType(p.billingCCNumber); + if (! isOnlyDigits(p.billingCSC) || + ! isLength(p.billingCSC, (ccType == 'amex' ? 4 : 3))) { + validationError("billingMeta", "Invalid CSC."); + } + // falling through here! + case 'invoice': + validateString(validationError, p.billingCountry, "billingCountry", "country name", true, 2); + validateString(validationError, p.billingAddressLine1, "billingAddressLine1", "billing address", true, 100); + validateString(validationError, p.billingAddressLine2, "billingAddressLine2", "billing address", false, 100); + validateString(validationError, p.billingCity, "billingCity", "city name", true, 40); + if (p.billingCountry == "US") { + validateString(validationError, p.billingState, "billingState", "state name", true, 2); + validateZip(validationError, p.billingZipCode); + } else { + validateString(validationError, p.billingProvince, "billingProvince", "province name", true, 40, 1); + validateString(validationError, p.billingPostalCode, "billingPostalCode", "postal code", true, 20, 5); + } + } +} + +function _cardType(number) { + var cardType = billingJS.getCcType(number); + switch (cardType) { + case 'visa': + return "Visa"; + case 'amex': + return "Amex"; + case 'disc': + return "Discover"; + case 'mc': + return "MasterCard"; + } +} + +function generatePayInfo(cart) { + var isUs = cart.billingCountry == "US"; + + var payInfo = { + cardType: _cardType(cart.billingCCNumber), + cardNumber: cart.billingCCNumber, + cardExpiration: ""+cart.billingExpirationMonth+"20"+cart.billingExpirationYear, + cardCvv: cart.billingCSC, + + nameSalutation: "", + nameFirst: cart.billingFirstName, + nameMiddle: "", + nameLast: cart.billingLastName, + nameSuffix: "", + + addressStreet: cart.billingAddressLine1, + addressStreet2: cart.billingAddressLine2, + addressCity: cart.billingCity, + addressState: (isUs ? cart.billingState : cart.billingProvince), + addressZip: (isUs ? cart.billingZipCode : cart.billingPostalCode), + addressCountry: cart.billingCountry + } + + return payInfo; +} + +var billingCartFieldMap = { + cardType: {f: ["billingCCNumber"], d: "credit card number"}, + cardNumber: { f: ["billingCCNumber"], d: "credit card number"}, + cardExpiration: { f: ["billingMeta", "billingMeta"], d: "expiration date" }, + cardCvv: { f: ["billingMeta"], d: "card security code" }, + card: { f: ["billingCCNumber", "billingMeta"], d: "credit card"}, + nameFirst: { f: ["billingFirstName"], d: "first name" }, + nameLast: {f: ["billingLastName"], d: "last name" }, + addressStreet: { f: ["billingAddressLine1"], d: "billing address" }, + addressStreet2: { f: ["billingAddressLine2"], d: "billing address" }, + addressCity: { f: ["billingCity"], d: "city" }, + addressState: { f: ["billingState", "billingProvince"], d: "state or province" }, + addressCountry: { f: ["billingCountry"], d: "country" }, + addressZip: { f: ["billingZipCode", "billingPostalCode"], d: "ZIP or postal code" }, + address: { f: ["billingAddressLine1", "billingAddressLine2", "billingCity", "billingState", "billingCountry", "billingZipCode"], d: "address" } +} + +function validateErrorFields(validationError, errorPrefix, fieldList) { + if (fieldList.length > 0) { + var errorMsg; + var errorFields; + errorMsg = errorPrefix + + fieldList.map(function(field) { return billingCartFieldMap[field].d }).join(", ") + + "."; + errorFields = []; + fieldList.forEach(function(field) { + errorFields = errorFields.concat(billingCartFieldMap[field].f); + }); + validationError(errorFields, errorMsg); + } +} + +function guessBillingNames(cart, name) { + if (! cart.billingFirstName && ! cart.billingLastName) { + var nameParts = name.split(/\s+/); + if (nameParts.length == 1) { + cart.billingFirstName = nameParts[0]; + } else { + cart.billingLastName = nameParts[nameParts.length-1]; + cart.billingFirstName = nameParts.slice(0, nameParts.length-1).join(' '); + } + } +} + +function writeToEncryptedLog(s) { + if (! appjet.config["etherpad.billingEncryptedLog"]) { + // no need to log, this probably isn't the live server. + return; + } + var e = net.appjet.oui.Encryptomatic; + sync.callsyncIfTrue(appjet.cache, + function() { return ! appjet.cache.billingEncryptedLog }, + function() { + appjet.cache.billingEncryptedLog = { + writer: new java.io.FileWriter(appjet.config["etherpad.billingEncryptedLog"], true), + key: e.readPublicKey("RSA", new java.io.FileInputStream(appjet.config["etherpad.billingPublicKey"])) + } + }); + var l = appjet.cache.billingEncryptedLog; + sync.callsync(l, function() { + l.writer.write(e.bytesToAscii(e.encrypt( + new java.io.ByteArrayInputStream((new java.lang.String(s)).getBytes("UTF-8")), + l.key))+"\n"); + l.writer.flush(); + }) +} + +function formatExpiration(expiration) { + return dateutils.shortMonths[Number(expiration.substr(0, 2))-1]+" "+expiration.substr(2); +} + +function formatDate(date) { + return dateutils.months[date.getMonth()]+" "+date.getDate()+", "+date.getFullYear(); +} + +function salesEmail(to, from, subject, headers, body) { + sendEmail(to, from, subject, headers, body); + if (globals.isProduction()) { + sendEmail("sales@pad.spline.inf.fu-berlin.de", from, subject, headers, body); + } +}
\ No newline at end of file diff --git a/trunk/etherpad/src/etherpad/store/eepnet_checkout.js b/trunk/etherpad/src/etherpad/store/eepnet_checkout.js new file mode 100644 index 0000000..62137d3 --- /dev/null +++ b/trunk/etherpad/src/etherpad/store/eepnet_checkout.js @@ -0,0 +1,101 @@ +/** + * Copyright 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS-IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import("email.sendEmail"); +import("sqlbase.sqlobj"); +import("stringutils"); + +import("etherpad.globals"); +import("etherpad.globals.*"); +import("etherpad.licensing"); +import("etherpad.utils.*"); +import("etherpad.store.checkout.*"); + +var COST_PER_USER = 99; +var SUPPORT_COST_PCT = 20; +var SUPPORT_MIN_COST = 50; + +function getPurchaseByEmail(email) { + return sqlobj.selectSingle('checkout_purchase', {email: email}); +} + +function hasEmailAlreadyPurchased(email) { + var purchase = getPurchaseByEmail(email); + return purchase && purchase.licenseKey ? true : false; +} + +function mailLostLicense(email) { + var purchase = getPurchaseByEmail(email); + if (purchase && purchase.licenseKey) { + sendLicenseEmail({ + email: email, + ownerName: purchase.owner, + orgName: purchase.organization, + licenseKey: purchase.licenseKey + }); + } +} + +function _updatePurchaseWithKey(id, key) { + sqlobj.updateSingle('checkout_purchase', {id: id}, {licenseKey: key}); +} + +function updatePurchaseWithReceipt(id, text) { + sqlobj.updateSingle('checkout_purchase', {id: id}, {receiptEmail: text}); +} + +function getPurchaseByInvoiceId(id) { + sqlobj.selectSingle('checkout_purchase', {invoiceId: id}); +} + +function generateLicenseKey(cart) { + var licenseKey = licensing.generateNewKey(cart.ownerName, cart.orgName, null, 2, cart.userCount); + cart.licenseKey = licenseKey; + _updatePurchaseWithKey(cart.customerId, cart.licenseKey); + return licenseKey; +} + +function receiptEmailText(cart) { + return renderTemplateAsString('email/eepnet_purchase_receipt.ejs', { + cart: cart, + dollars: dollars, + obfuscateCC: obfuscateCC + }); +} + +function licenseEmailText(userName, licenseKey) { + return renderTemplateAsString('email/eepnet_license_info.ejs', { + userName: userName, + licenseKey: licenseKey, + isEvaluation: false + }); +} + +function sendReceiptEmail(cart) { + var receipt = cart.receiptEmail || receiptEmailText(cart); + + salesEmail(cart.email, "sales@pad.spline.inf.fu-berlin.de", + "EtherPad: Receipt for "+cart.ownerName+" ("+cart.orgName+")", + {}, receipt); +} + +function sendLicenseEmail(cart) { + var licenseEmail = licenseEmailText(cart.ownerName, cart.licenseKey); + + salesEmail(cart.email, "sales@pad.spline.inf.fu-berlin.de", + "EtherPad: License Key for "+cart.ownerName+" ("+cart.orgName+")", + {}, licenseEmail); +}
\ No newline at end of file diff --git a/trunk/etherpad/src/etherpad/store/eepnet_trial.js b/trunk/etherpad/src/etherpad/store/eepnet_trial.js new file mode 100644 index 0000000..570d351 --- /dev/null +++ b/trunk/etherpad/src/etherpad/store/eepnet_trial.js @@ -0,0 +1,241 @@ +/** + * Copyright 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS-IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import("email.sendEmail"); +import("sqlbase.sqlobj"); +import("sqlbase.sqlcommon"); +import("execution"); + +import("etherpad.sessions.getSession"); +import("etherpad.log"); +import("etherpad.licensing"); +import("etherpad.utils.*"); +import("etherpad.globals.*"); + +//---------------------------------------------------------------- + +function getTrialDays() { + return 30; +} + +function getTrialUserQuota() { + return 100; +} + +function mailLicense(data, licenseKey, expiresDate) { + var toAddr = data.email; + if (isTestEmail(toAddr)) { + toAddr = "blackhole@appjet.com"; + } + var subject = ('EtherPad: Trial License Information for '+ + data.firstName+' '+data.lastName+' ('+data.orgName+')'); + + var emailBody = renderTemplateAsString("email/eepnet_license_info.ejs", { + userName: data.firstName+" "+data.lastName, + licenseKey: licenseKey, + expiresDate: expiresDate, + isEvaluation: true + }); + + sendEmail( + toAddr, + 'sales@pad.spline.inf.fu-berlin.de', + subject, + {}, + emailBody + ); +} + +function mailLostLicense(email) { + var data = sqlobj.selectSingle('eepnet_signups', {email: email}); + var keyInfo = licensing.decodeLicenseInfoFromKey(data.licenseKey); + var expiresDate = keyInfo.expiresDate; + + mailLicense(data, data.licenseKey, expiresDate); +} + +function hasEmailAlreadyDownloaded(email) { + var existingRecord = sqlobj.selectSingle('eepnet_signups', {email: email}); + if (existingRecord) { + return true; + } else { + return false + } +} + +function createAndMailNewLicense(data) { + sqlcommon.inTransaction(function() { + var expiresDate = new Date(+(new Date)+(1000*60*60*24*getTrialDays())); + var licenseKey = licensing.generateNewKey( + data.firstName + ' ' + data.lastName, + data.orgName, + +expiresDate, + licensing.getEditionId('PRIVATE_NETWORK_EVALUATION'), + getTrialUserQuota() + ); + + // confirm key + if (!licensing.isValidKey(licenseKey)) { + throw Error("License key I just created is not valid: "+l); + } + + // Log all this precious info + _logDownloadData(data, licenseKey); + + // Store in database + sqlobj.insert("eepnet_signups", { + firstName: data.firstName, + lastName: data.lastName, + email: data.email, + orgName: data.orgName, + jobTitle: data.jobTitle, + date: new Date(), + signupIp: String(request.clientAddr).substr(0,16), + estUsers: data.estUsers, + licenseKey: licenseKey, + phone: data.phone, + industry: data.industry + }); + + mailLicense(data, licenseKey, expiresDate); + + // Send sales notification + var clientAddr = request.clientAddr; + var initialReferer = getSession().initialReferer; + execution.async(function() { + _sendSalesNotification(data, clientAddr, initialReferer); + }); + + }); // end transaction +} + +function _logDownloadData(data, licenseKey) { + log.custom("eepnet_download_info", { + email: data.email, + firstName: data.firstName, + lastName: data.lastName, + org: data.orgName, + jobTitle: data.jobTitle, + phone: data.phone, + estUsers: data.estUsers, + licenseKey: licenseKey, + ip: request.clientAddr, + industry: data.industry, + referer: getSession().initialReferer + }); +} + +function getWeb2LeadData(data, ip, ref) { + var googleQuery = extractGoogleQuery(ref); + var w2ldata = { + oid: "00D80000000b7ey", + first_name: data.firstName, + last_name: data.lastName, + email: data.email, + company: data.orgName, + title: data.jobTitle, + phone: data.phone, + '00N80000003FYtG': data.estUsers, + '00N80000003FYto': ref, + '00N80000003FYuI': googleQuery, + lead_source: 'EEPNET Download', + industry: data.industry + }; + + if (!isProduction()) { +// w2ldata.debug = "1"; +// w2ldata.debugEmail = "aaron@appjet.com"; + } + + return w2ldata; +} + +function _sendSalesNotification(data, ip, ref) { + var hostname = ipToHostname(ip) || "unknown"; + + var subject = "EEPNET Trial Download: "+[data.orgName, data.firstName + ' ' + data.lastName, data.email].join(" / "); + + var body = [ + "", + "This is an automated message.", + "", + "Somebody downloaded a "+getTrialDays()+"-day trial of EEPNET.", + "", + "This lead should be automatically added to the AppJet salesforce account.", + "", + "Organization: "+data.orgName, + "Industry: "+data.industry, + "Full Name: "+data.firstName + ' ' + data.lastName, + "Job Title: "+data.jobTitle, + "Email: "+data.email, + 'Phone: '+data.phone, + "Est. Users: "+data.estUsers, + "IP Address: "+ip+" ("+hostname+")", + "Session Referer: "+ref, + "" + ].join("\n"); + + var toAddr = 'sales@pad.spline.inf.fu-berlin.de'; + if (isTestEmail(data.email)) { + toAddr = 'blackhole@appjet.com'; + } + sendEmail( + toAddr, + 'sales@pad.spline.inf.fu-berlin.de', + subject, + {'Reply-To': data.email}, + body + ); +} + +function getSalesforceIndustryList() { + return [ + '--None--', + 'Agriculture', + 'Apparel', + 'Banking', + 'Biotechnology', + 'Chemicals', + 'Communications', + 'Construction', + 'Consulting', + 'Education', + 'Electronics', + 'Energy', + 'Engineering', + 'Entertainment', + 'Environmental', + 'Finance', + 'Food & Beverage', + 'Government', + 'Healthcare', + 'Hospitality', + 'Insurance', + 'Machinery', + 'Manufacturing', + 'Media', + 'Not For Profit', + 'Other', + 'Recreation', + 'Retail', + 'Shipping', + 'Technology', + 'Telecommunications', + 'Transportation', + 'Utilities' + ]; +} + |