summaryrefslogtreecommitdiffstats
path: root/trunk/etherpad/src/etherpad/store
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/etherpad/src/etherpad/store')
-rw-r--r--trunk/etherpad/src/etherpad/store/checkout.js300
-rw-r--r--trunk/etherpad/src/etherpad/store/eepnet_checkout.js101
-rw-r--r--trunk/etherpad/src/etherpad/store/eepnet_trial.js241
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'
+ ];
+}
+