Skip to content
This repository has been archived by the owner on Aug 10, 2023. It is now read-only.

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
aleemstreak committed Mar 18, 2015
0 parents commit ac71155
Show file tree
Hide file tree
Showing 17 changed files with 465 additions and 0 deletions.
216 changes: 216 additions & 0 deletions app.js
@@ -0,0 +1,216 @@
var cachedCustomerPromises = {};

var seenSidebarEmails = new WeakMap();
var sidebarForThread = new WeakMap();

var stripeInfoPromise = null;

var sidebarTemplatePromise = null;


InboxSDK.load('1', 'stripe').then(function(sdk) {
stripeInfoPromise = getStripeInfo();

sdk.Lists.registerThreadRowViewHandler(function(threadRowView) {
var contacts = threadRowView.getContacts();
for (var i = 0; i < contacts.length; i++) {
var contact = contacts[i];
getStripeCustomerWithoutMyDomain(contact, sdk.User.getEmailAddress()).then(function(customer) {
if (customer != null) {
addStripeIndicatorToThreadRow(threadRowView, contact.emailAddress);
}
});
}
});

sdk.Conversations.registerMessageViewHandler(function(messageView) {
var threadView = messageView.getThreadView();
if (!seenSidebarEmails.has(threadView)) {
seenSidebarEmails.set(threadView, []);
}

var contacts = messageView.getRecipients();
contacts.push(messageView.getSender());

for (var i = 0; i < contacts.length; i++) {
var contact = contacts[i];
if (seenSidebarEmails.get(threadView).indexOf(contact.emailAddress) != -1) {
continue;
}
seenSidebarEmails.get(threadView).push(contact.emailAddress);


getStripeCustomerWithoutMyDomain(contact, sdk.User.getEmailAddress()).then(function(customer) {
if (customer != null) {
addStripeSidebar(threadView, customer);
}
});
}
});

});


function addStripeSidebar(threadView, customer) {
if (!sidebarForThread.has(threadView)) {
sidebarForThread.set(threadView, document.createElement('div'));

threadView.addSidebarContentPanel({
el: sidebarForThread.get(threadView),
title: "Stripe Customers",
iconUrl: chrome.runtime.getURL('stripe.png')
});
}

if (!sidebarTemplatePromise) {
sidebarTemplatePromise = get(chrome.runtime.getURL('sidebarTemplate.html'), null, null);
}

Promise.all([
stripeGet("https://dashboard.stripe.com/ajax/proxy/api/v1/invoices", {customer: customer.id, count: 100, limit: 100}),
stripeGet("https://dashboard.stripe.com/ajax/proxy/api/v1/charges", {customer: customer.id, count: 100, limit: 100}),
stripeGet("https://dashboard.stripe.com/ajax/proxy/api/v1/customers/" + customer.id + "/subscriptions", {count: 100, limit: 100}),
sidebarTemplatePromise
])
.then(function(results) {

var invoices = results[0];
var charges = results[1];
var subscriptions = results[2];
var html = results[3];


transformCustomer(customer);
transformSubscriptions(subscriptions);
transformInvoices(invoices);
transformCharges(charges);
var stats = createStats(customer, subscriptions, invoices, charges);

var template = _.template(html);
sidebarForThread.get(threadView).innerHTML = sidebarForThread.get(threadView).innerHTML + template({
customer: customer,
invoices: invoices,
subscriptions: subscriptions,
charges: charges,
stats: stats
});
});

}

function formatStripeCurrency(amount, digits) {
return (amount/100).toLocaleString('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits: digits, minimumFractionDigits: digits });
}

function transformCustomer(customer) {
for (var i = 0; i < customer.sources.data.length; i++) {
var source = customer.sources.data[i];
source.imageUrl = chrome.runtime.getURL('cards/' + source.brand + '.png');
}
customer.dashboardURL = "https://dashboard.stripe.com/customers/" + customer.id;
}

function transformSubscriptions(subscriptions) {
_.sortBy(subscriptions.data, 'start');
for (var i = 0; i < subscriptions.data.length; i++) {
var sub = subscriptions.data[i];
sub.discountedPrice = sub.plan.amount;
if (sub.discount && sub.discount.coupon && sub.discount.coupon.valid) {
if (sub.discount.coupon.amount_off) {
sub.discountedPrice = sub.discountedPrice - sub.discount.coupon.amount_off;
}
else if (sub.discount.coupon.percent_off) {
sub.discountedPrice = sub.discountedPrice * (100 - sub.discount.coupon.percent_off) / 100;
}
}
}
}

function transformInvoices(invoices) {
for (var i = 0; i < invoices.data.length; i++) {
var inv = invoices.data[i];
inv.imageUrl = inv.paid ? chrome.runtime.getURL('paid.png') : chrome.runtime.getURL('unpaid.png');
}
}

function transformCharges(charges) {
for (var i = 0; i < charges.data.length; i++) {
var chg = charges.data[i];
chg.imageUrl = chg.status == "succeeded" ? chrome.runtime.getURL('paid.png') : chrome.runtime.getURL('unpaid.png');
}
}

function createStats(customer, subscriptions, invoices, charges) {
var mostRecentSub = subscriptions.data[subscriptions.data.length - 1];
var retVal = {
totalSpend: 0,
currentMRR: (mostRecentSub ? mostRecentSub.discountedPrice : 0)
};

_.each(charges.data, function(chg) {
retVal.totalSpend += (chg.amount - chg.amount_refunded);
});

return retVal;
}

function addStripeIndicatorToThreadRow(threadRowView, email) {
threadRowView.addImage({
imageUrl: chrome.runtime.getURL('stripe.png'),
tooltip: email,
imageClass: "rounded_stripe"
});
}

function get(url, params, headers) {
return Promise.resolve(
$.ajax({
url: url,
type: "GET",
data: params,
headers: headers
})
);
}

function stripeGet(url, params) {
return stripeInfoPromise.then(function(info) {
var headers = {
"Authorization": ("Bearer " + info.session_api_key),
"x-stripe-livemode": true
};
return get(url, params, headers);
});
}

function getStripeCustomerWithoutMyDomain(contact, currentUserEmail) {
if (!cachedCustomerPromises[contact.emailAddress]) {
if (contact.emailAddress.split("@")[1] === currentUserEmail.split("@")[1]) {
cachedCustomerPromises[contact.emailAddress] = new Promise(function(resolve, reject) {
resolve(null);
});
}
else {
cachedCustomerPromises[contact.emailAddress] = stripeGet('https://dashboard.stripe.com/ajax/proxy/api/v1/search', {count:20, query:contact.emailAddress})
.then(function(result){
for (var i = 0; i < result.data.length; i++) {
if (result.data[i].object == "customer") {
result.data[i].name = contact.name;
return result.data[i];
}
}
return null;
});
}
}
return cachedCustomerPromises[contact.emailAddress];
}

function getStripeInfo() {
return get('https://dashboard.stripe.com/dashboard', {}).then(function(response){
var e1 = document.createElement("div"), e2 = document.createElement("div");
e1.innerHTML = response;
e2.innerHTML = e1.querySelector('#preloaded_json').text;
return JSON.parse(e2.textContent);
});
}
Binary file added cards/American Express.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added cards/Diners Club.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added cards/Discover.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added cards/JCB.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added cards/MasterCard.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added cards/Unknown.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added cards/Visa.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions inboxsdk.js

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions jquery.js

Large diffs are not rendered by default.

32 changes: 32 additions & 0 deletions manifest.json
@@ -0,0 +1,32 @@
{
"name": "Stripe for Gmail",
"description": "Show Stripe customer information in Gmail",
"version": "0.5",
"icons": {
"128": "stripe.png"
},
"content_scripts": [
{
"matches": [
"https://mail.google.com/*",
"https://inbox.google.com/*"
],
"js": ["jquery.js", "underscore.js", "inboxsdk.js","app.js"],
"css": ["styles.css"],
"run_at": "document_end"
}
],
"permissions": [
"https://mail.google.com/",
"https://inbox.google.com/",
"https://*.stripe.com/"
],
"web_accessible_resources": [
"stripe.png",
"paid.png",
"unpaid.png",
"cards/*",
"sidebarTemplate.html"
],
"manifest_version": 2
}
Binary file added paid.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
110 changes: 110 additions & 0 deletions sidebarTemplate.html
@@ -0,0 +1,110 @@
<div class="column person">
<div class="name"><%- customer.name %></div>
<div><%- customer.email %></div>
<a class="gmail_simple_button" href="<%- customer.dashboardURL %>" target="_blank">
<div role="button" class="J-J5-Ji T-I T-I-ax7 ayk-axZ-aup-I-UR ayk-axZ-aup-I ayk-axZ-aup-at2 T-I-JW T-I-Zf-aw2" style="visibility: visible; -webkit-user-select: none;">
<span class="Txql0d ayk-axZ-aup-I-Jm-UR">View on Stripe</span>
</div>
</a>
</div>

<div class="row">
<div class="column">
<div class="num"><%- formatStripeCurrency(stats.currentMRR, 0) %></div>
<div class="num_description">MRR</div>
</div>
<div class="spacer"></div>
<div class="column">
<div class="num"><%- formatStripeCurrency(stats.totalSpend, 0) %></div>
<div class="num_description">Total</div>
</div>
</div>



<div class="stripe_sidebar_section">
<p class="title">Active Subscriptions</p>
<%
_.each(subscriptions.data, function(sub) {
if (sub.status = "active") {
%>
<div class="row main">
<div class="name"><%- sub.quantity %> x <%- sub.plan.name %></div>
<div class="spacer"></div>
<div class="details">(<%- formatStripeCurrency(sub.discountedPrice, 0) %>/month)</div>
</div>

<div class="row secondary">
<div class="spacer"></div>
<!-- <div class="actions">Edit | Cancel</div> -->
</div>
<% }
});
%>
<div>

<div class="stripe_sidebar_section">
<p class="title">Recent Invoices</p>
<%
_.each(invoices.data, function(inv, i) {
if (i > 4) return;
%>
<div class="row main">
<div class="name"><%- formatStripeCurrency(inv.total, 2) %></div>
<div class="spacer"></div>
<div class="details"><%- new Date(inv.date*1000).toLocaleDateString() %></div>
</div>

<div class="row secondary">
<img class="pay_status" src="<%- inv.imageUrl %>"></img>
<div class="description row"><%- inv.paid ? "Paid" : "Unpaid" %></div>
<div class="spacer"></div>
<!-- <div class="actions">Refund</div> -->
</div>
<%
});
%>
</div>

<div class="stripe_sidebar_section">
<p class="title">Recent Charges</p>
<%
_.each(charges.data, function(chg, i) {
if (i > 4) return;
%>
<div class="row main">
<div class="name"><%- formatStripeCurrency(chg.amount, 2) %></div>
<div class="spacer"></div>
<div class="details"><%- new Date(chg.created*1000).toLocaleDateString() %></div>
</div>

<div class="row secondary">
<img class="pay_status" src="<%- chg.imageUrl %>"></img>
<div class="description row"><%- chg.status == "succeeded" ? "Charged" : "Failed" %></div>
<div class="spacer"></div>
</div>
<%
});
%>
</div>


<div class="stripe_sidebar_section">
<p class="title">Cards</p>
<%
if (!customer.sources.data) {
return;
}
_.each(customer.sources.data, function(source) {

%>
<div class="row main">
<img class="card" src="<%- source.imageUrl %>"></img>
<div class="name row">**** **** **** <%- source.last4 %></div>
<div class="spacer"></div>
<div class="details"><%- source.exp_month %>/<%- source.exp_year %></div>
</div>
<%
});
%>
</div>
Binary file added stripe.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit ac71155

Please sign in to comment.