Add core merchant functionality

master
Skylar Ittner 5 years ago
parent 9fa968cd69
commit 6ff5fd50c7

@ -1,7 +1,7 @@
Helping Helena App
Helping Helena Merchant App
============
This is a cross-platform app for Helping Helena. It can be
This is a cross-platform app for Helping Helena. It enables merchants to accept Helping Helena as a payment method. It can be
built for most platforms by using either
[Apache Cordova](https://cordova.apache.org) or [NW.js](https://nwjs.io).

@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<widget id="com.netsyms.HelpingHelena" version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:cdv="http://cordova.apache.org/ns/1.0">
<widget id="com.netsyms.HelpingHelenaMerchant" version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:cdv="http://cordova.apache.org/ns/1.0">
<name>Helping Helena</name>
<description>
</description>

@ -3,11 +3,11 @@ auxiliary.org-netbeans-modules-cordova.phonegap=true
auxiliary.org-netbeans-modules-javascript-nodejs.enabled=false
auxiliary.org-netbeans-modules-javascript-nodejs.node_2e_default=true
auxiliary.org-netbeans-modules-javascript-nodejs.sync_2e_enabled=true
file.reference.HelpingHelena-test=test
file.reference.HelpingHelena-www=www
file.reference.HelpingHelenaMerchant-test=test
file.reference.HelpingHelenaMerchant-www=www
files.encoding=UTF-8
project.license=mpl
project.licensePath=./nbproject/license-mpl.txt
project.organization=Netsyms Technologies
site.root.folder=${file.reference.HelpingHelena-www}
test.folder=${file.reference.HelpingHelena-test}
site.root.folder=${file.reference.HelpingHelenaMerchant-www}
test.folder=${file.reference.HelpingHelenaMerchant-test}

@ -3,7 +3,7 @@
<type>org.netbeans.modules.web.clientproject</type>
<configuration>
<data xmlns="http://www.netbeans.org/ns/clientside-project/1">
<name>HelpingHelena</name>
<name>HelpingHelena Merchant</name>
</data>
</configuration>
</project>

@ -1,6 +1,6 @@
{
"name": "com.netsyms.HelpingHelena",
"displayName": "HelpingHelena",
"name": "com.netsyms.HelpingHelenaMerchant",
"displayName": "HelpingHelena Merchant",
"version": "1.0.0",
"description": "",
"main": "index.js",
@ -35,4 +35,4 @@
"android"
]
}
}
}

@ -76,104 +76,7 @@ html.md .navbar .link {
color: white;
}
.card-expandable .card-header {
min-height: var(--f7-card-header-min-height);
color: var(--f7-card-header-text-color);
font-size: var(--f7-card-header-font-size);
font-weight: var(--f7-card-header-font-weight);
padding: var(--f7-card-header-padding-vertical) var(--f7-card-header-padding-horizontal);
}
.card-expandable .card-header:after {
display: block !important;
}
.card-opened .card-header {
display: none;
}
#qrcode, .card-closing #qrcode {
width: auto;
#qrcode {
max-height: calc(100% - var(--f7-navbar-height));
max-width: 100%;
height: 12em;
transition: height 0.3s ease-in-out;
}
.card-opening #qrcode, .card-opened #qrcode {
height: 55em;
max-height: 55vh;
}
#map-card {
background-color: hsl(47, 26%, 88%);
}
#map {
position: absolute;
top: 0;
left: 0;
height: 25em;
width: 100%;
opacity: 1;
transition: opacity 0.5s linear, top 0.25s linear;
background-color: hsl(47, 26%, 88%);
}
.card-expandable.card-closing, .card-expandable.card-opened, .card-expandable.card-opening {
z-index: 9999999;
}
.card-opening #map, .card-closing #map {
opacity: 0;
}
.card-opened #map {
opacity: 1;
top: var(--f7-navbar-height);
height: calc(100% - var(--f7-navbar-height));
}
.card-backdrop {
z-index: 999999;
}
#map .leaflet-control {
opacity: 0;
visibility: hidden;
transition: visibility 0s, opacity 0.5s;
}
.card-opened #map .leaflet-control {
opacity: 1;
visibility: visible;
}
.card-expandable .navbar {
display: none;
}
.card-opened .navbar {
display: block;
}
#person-name {
overflow-wrap: break-word;
}
#person-bio {
border-top: 1px solid var(--f7-card-footer-border-color);
padding-top: var(--f7-card-footer-padding-vertical);
overflow-wrap: break-word;
}
.fa-map-icon {
padding: 2px;
color: white;
background-color: var(--f7-theme-color);
border: 2px solid var(--f7-theme-color-shade);
border-radius: 50%;
}
.fa-map-icon i {
margin-top: 0.4em;
}

@ -15,7 +15,7 @@
<link rel="stylesheet" href="node_modules/mapbox-gl/dist/mapbox-gl.css">
<link rel="stylesheet" href="css/styles.css">
<title>Helping Helena</title>
<title>Helping Helena Merchant</title>
<div id="app" class="color-theme-orange">
@ -30,9 +30,6 @@
<script src="node_modules/jquery/dist/jquery.min.js"></script>
<script src="node_modules/framework7/js/framework7.bundle.min.js"></script>
<script src="node_modules/qrcode-generator/qrcode.js"></script>
<script src="node_modules/leaflet/dist/leaflet.js"></script>
<script src="node_modules/mapbox-gl/dist/mapbox-gl.js"></script>
<script src="node_modules/mapbox-gl-leaflet/leaflet-mapbox-gl.js"></script>
<script src="settings.js"></script>
<script src="js/api.js"></script>

@ -1,87 +0,0 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
var stripe = null;
var elements = null;
var card = null;
function initPaymentPage() {
$("#tx-off-percent").text(+(SETTINGS["addfunds_percent"] * 100).toFixed(2));
initStripe();
}
function initStripe() {
// Wait for Stripe to be loaded
if (typeof Stripe == 'undefined') {
setTimeout(initStripe, 500);
return;
}
stripe = Stripe(SETTINGS["stripe_pubkey"]);
elements = stripe.elements();
card = elements.create('card', {});
card.mount('#card-element');
card.addEventListener('change', function (event) {
var displayError = document.getElementById('card-errors');
if (event.error) {
displayError.textContent = event.error.message;
} else {
displayError.textContent = '';
}
});
}
function addFeesToAmount() {
if ($("#funds-amount").val() == "") {
return;
}
var amount = $("#funds-amount").val() * 1.0;
var stripefees = 0.30 + (amount * 0.029);
var totalfees = amount * SETTINGS["addfunds_percent"];
amount = (amount + (stripefees > totalfees ? 0.30 : 0)) /
(1 - (stripefees > totalfees ? 0.029 : SETTINGS["addfunds_percent"]));
$("#funds-amount").val(amount.toFixed(2));
}
function chargeCard() {
if ($("#funds-amount").val() == "") {
return;
}
if (stripe == null) {
return;
}
app.preloader.show();
stripe.createToken(card).then(function (result) {
if (result.error) {
// Inform the customer that there was an error.
app.preloader.hide();
var errorElement = document.getElementById('card-errors');
errorElement.textContent = result.error.message;
} else {
callAPI("addfunds", {
key: localStorage.getItem("key"),
amount: $("#funds-amount").val(),
token: result.token.id
}, function (response) {
app.preloader.hide();
router.navigate("/fundsadded/" + response.charged_amount.toFixed(2) + "/" + response.final_amount.toFixed(2));
}, function (error) {
app.preloader.hide();
app.dialog.alert(error, "Error");
});
}
});
}
$(".preset-amount-button").click(function () {
$($(this).data("target")).val($(this).data("amount"));
});

@ -1,69 +0,0 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
var leafletnearbylayer = L.geoJson(
{
name: "Nearby People",
type: "FeatureCollection",
features: [
{
type: "Feature",
geometry: {
type: "Point",
coordinates: [0, 0]
},
properties: {
id: "",
name: "",
username: "",
verified: false
}
}
]
},
{
onEachFeature: function (feature, layer) {
if (feature.properties && feature.properties.popupContent) {
layer.bindPopup(feature.properties.popupContent);
}
},
pointToLayer: function (feature, latlng) {
var marker = L.marker(latlng, {
icon: L.icon({
iconUrl: "img/mapmarker.svg",
iconSize: [25, 41],
iconAnchor: [12.5, 40],
popupAnchor: [0, -28]
})
});
return marker;
}
}
);
function updateMap() {
var diagonalMeters = leafletmap.getBounds().getNorthWest().distanceTo(leafletmap.getBounds().getSouthEast());
var radius = (diagonalMeters / 2.0) / 1609.344;
callAPIRawResponse("getnearby", {
key: localStorage.getItem("key"),
latitude: leafletmap.getCenter().lat,
longitude: leafletmap.getCenter().lng,
radius: radius,
format: "geojson"
}, function (data) {
if (data.type == "FeatureCollection") {
leafletnearbylayer.clearLayers();
data.features.forEach(function (item) {
item.properties.popupContent = "<i class=\"fas fa-user\"></i> " + (item.properties.name.length > 50 ? item.properties.name.substring(0, 50) + "..." : item.properties.name) + "<br><br><a class=\"button button-small button-fill text-color-white card-prevent-open\" href=\"/sendmoney/" + item.properties.id + "\">Send Money</a>";
leafletnearbylayer.addData(item);
});
leafletmap.addLayer(leafletnearbylayer);
$(".leaflet-marker-icon").addClass("card-prevent-open");
}
});
}

@ -4,64 +4,42 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
var accountBalance = 0.0;
// Set to true to stop pinging the server for tx status
var stopPaymentCheck = false;
$(".view-main").on("ptr:refresh", ".ptr-content", function () {
loadHomePage(function () {
setTimeout(app.ptr.done, 500);
});
});
$(".view-main").on("card:open", ".card-expandable", function () {
$(".ptr-preloader").addClass("display-none");
$("#payment-popup").on("popup:close", function () {
stopPaymentCheck = true;
});
$("#map-card").on("card:closed card:opened", function () {
leafletmap.invalidateSize();
});
$("#map-card").on("card:open", function () {
leafletgllayer._glMap.setPitch(20);
});
$("#map-card").on("card:close", function () {
leafletgllayer._glMap.setPitch(0);
});
$(".view-main").on("card:closed", ".card-expandable", function () {
$(".ptr-preloader").removeClass("display-none");
});
function loadBalance(callback) {
$("#balance-error-icon").addClass("display-none");
$("#balance-loading").removeClass("display-none");
callAPI("checkbalance", {
key: localStorage.getItem("key")
}, function (data) {
accountBalance = data.balance;
$("#balance-amount").text(data.balance);
$("#balance-loading").addClass("display-none");
$("#balance-error").text("");
callback(true);
}, function (msg) {
$("#balance-loading").addClass("display-none");
$("#balance-error-icon").css("display", "");
$("#balance-error").text(msg);
callback(false, msg);
$("#showcodebtn").click(function () {
var amount = $("#amount-box").val() * 1.0;
if (amount <= 0) {
app.dialog.alert("Please specify an amount.", "Error");
return;
} else if (amount > 999.99) {
app.dialog.alert("Please specify an amount less than $999.99.", "Error");
return;
}
app.preloader.show();
makeQrCode(amount, function (ok, msg) {
app.preloader.hide();
if (!ok) {
app.dialog.alert(msg, "Error");
} else {
app.popup.open($("#payment-popup"));
}
});
}
});
function loadQrCode(callback) {
$("#receive-loading").removeClass("display-none");
callAPI("getprofile", {
function makeQrCode(amount, callback) {
callAPI("gettxcode", {
key: localStorage.getItem("key"),
amount: amount
}, function (data) {
app.preloader.hide();
//if (data.profile.type * 1 > 1) {
var typeNumber = 4;
var errorCorrectionLevel = 'L';
var qr = qrcode(typeNumber, errorCorrectionLevel);
qr.addData(SETTINGS['webapp_url'] + '?sendto=' + data.profile.publicid);
qr.addData(SETTINGS['webapp_url'] + '?sendto=' + data.txcode + "&amount=" + data.amount);
qr.make();
var svg = qr.createSvgTag({
margin: 6,
@ -69,48 +47,36 @@ function loadQrCode(callback) {
});
var base64 = window.btoa(svg);
$("#qrcode").attr("src", 'data:image/svg+xml;base64,' + base64);
$("#qrcode-label").text(data.profile.publicid);
$("#receive-loading").addClass("display-none");
$("#pay-amount").text(data.amount);
$("#paid-amount").text(data.amount);
app.preloader.hide();
checkPaymentStatus(data.txcode);
callback(true);
//}
}, function (msg) {
$("#receive-loading").addClass("display-none");
callback(false, msg);
});
}
function broadcastLocation() {
getLocation(function (position) {
callAPI("broadcast", {
key: localStorage.getItem("key"),
latitude: position.coords.latitude,
longitude: position.coords.longitude
}, function (success) {
app.toast.create({
text: "<i class=\"fas fa-check\"></i> Location sent!",
closeTimeout: 2000
}).open();
}, function (error) {
app.dialog.alert(error, "Error");
});
}, function (error) {
app.dialog.alert(error, "Location Error");
});
}
function loadHomePage(callback) {
var done = 0;
var total = 2;
loadBalance(function () {
done++;
if (done >= total) {
callback();
}
});
loadQrCode(function () {
done++;
if (done >= total) {
callback();
function checkPaymentStatus(txcode) {
if (stopPaymentCheck) {
stopPaymentCheck = false;
return;
}
callAPI("gettxstatus", {
key: localStorage.getItem("key"),
txcode: txcode
}, function (data) {
if (data.complete == true) {
app.popup.close($("#payment-popup"));
app.popup.open($("#paid-popup"));
} else {
setTimeout(function () {
checkPaymentStatus(txcode);
}, 15 * 100);
}
}, function (msg) {
app.popup.close($("#payment-popup"));
app.popup.close($("#paid-popup"));
app.dialog.alert(msg, "Error");
});
}

@ -0,0 +1,49 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
$('#username').on("keyup", function () {
$('#username').val($('#username').val().toLowerCase());
});
function checkAndSave(username, password) {
app.preloader.show();
callAPI("getkey", {
username: username,
password: password
}, function (data) {
app.preloader.hide();
callAPI("getprofile", {
key: data.key
}, function (profile) {
if (profile.profile.type != 3) {
app.preloader.hide();
app.dialog.alert("You downloaded the merchant app but you aren't a merchant. Go to helpinghelena.org to get the correct app.", "Error");
return;
}
localStorage.setItem("key", data.key);
localStorage.setItem("username", username);
localStorage.setItem("password", password);
localStorage.setItem("configured", true);
// Restart the app to re-read the config
restartApplication();
}, function (msg) {
app.preloader.hide();
app.dialog.alert(msg, "Error");
});
}, function (msg) {
app.preloader.hide();
app.dialog.alert(msg, "Error");
});
}
function doLogin() {
var type = 3;
var username = $("#username").val();
var password = $("#password").val();
checkAndSave(username, password, type);
}

@ -121,39 +121,19 @@ function setupKeyRefresh() {
if (localStorage.getItem("configured") == null) {
// Open the setup page
router.navigate("/setup/0");
router.navigate("/login");
} else {
checkKey(function (valid) {
if (valid) {
try {
var url = new URL(window.location.href);
if (typeof url.searchParams.get("sendto") == "string") {
router.navigate("/sendmoney/" + url.searchParams.get("sendto"));
} else {
router.navigate("/home");
}
} catch (ex) {
router.navigate("/home");
}
router.navigate("/home");
setupKeyRefresh();
} else {
refreshKey(function (ok) {
if (ok) {
try {
var url = new URL(window.location.href);
if (typeof url.searchParams.get("sendto") == "string") {
router.navigate("/sendmoney/" + url.searchParams.get("sendto"));
} else {
router.navigate("/home");
}
} catch (ex) {
router.navigate("/home");
}
router.navigate("/home");
setupKeyRefresh();
} else {
router.navigate("/setup/0");
router.navigate("/login");
}
});
}

@ -1,68 +0,0 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
var userPositionIsReal = false;
var userposition = {
coords: {
latitude: 46.595,
longitude: -112.019,
accuracy: 10000
}
};
var leafletmap = L.map('map', {
minZoom: 12,
maxZoom: 20
});
leafletmap.attributionControl.setPrefix("");
var leafletgllayer = L.mapboxGL({
attribution: "© OpenMapTiles © OpenStreetMap contributors",
accessToken: 'none',
style: SETTINGS["map_style_json"],
pitch: 0
});
leafletgllayer.addTo(leafletmap);
function recenterMapPosition() {
var zoom = 13;
if (userposition.coords.accuracy < 1000) {
zoom = 15;
}
leafletmap.setView([userposition.coords.latitude, userposition.coords.longitude], zoom);
}
function recenterMapToUserPosition() {
if (userPositionIsReal == false) {
getLocation(function (position) {
userposition = position;
userPositionIsReal = true;
recenterMapPosition();
});
} else {
recenterMapPosition();
}
}
// Attempt to get user location
watchLocation(function (position) {
userposition = position;
if (userPositionIsReal == false) {
recenterMapPosition();
userPositionIsReal = true;
}
});
// Set map to default position
recenterMapPosition();
// Load nearby
updateMap();
// Watch for map moving
leafletmap.on("moveend", function () {
updateMap();
});

@ -1,76 +0,0 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
var leafletnearbylayer = L.geoJson(
{
name: "Nearby Merchants",
type: "FeatureCollection",
features: [
{
type: "Feature",
geometry: {
type: "Point",
coordinates: [0, 0]
},
properties: {
id: "",
name: "",
address: "",
types: {}
}
}
]
},
{
onEachFeature: function (feature, layer) {
if (feature.properties && feature.properties.popupContent) {
layer.bindPopup(feature.properties.popupContent);
}
},
pointToLayer: function (feature, latlng) {
var icon = "fas fa-store";
if (feature.properties.types.length > 0) {
icon = feature.properties.types[0].icon;
}
var marker = L.marker(latlng, {
icon: (new L.divIcon({
html: '<i class="' + icon + ' fa-fw"></i>',
iconSize: [20, 20],
className: 'fa-map-icon'
}))
});
return marker;
}
}
);
function updateMap() {
var diagonalMeters = leafletmap.getBounds().getNorthWest().distanceTo(leafletmap.getBounds().getSouthEast());
var radius = (diagonalMeters / 2.0) / 1609.344;
callAPIRawResponse("getmerchants", {
key: localStorage.getItem("key"),
latitude: leafletmap.getCenter().lat,
longitude: leafletmap.getCenter().lng,
radius: radius,
format: "geojson"
}, function (data) {
if (data.type == "FeatureCollection") {
leafletnearbylayer.clearLayers();
data.features.forEach(function (item) {
var icons = "";
item.properties.types.forEach(function (type) {
icons += "<i class=\"" + type.icon + "\"></i> " + type.name + "<br>";
});
item.properties.popupContent = (item.properties.name.length > 50 ? item.properties.name.substring(0, 50) + "..." : item.properties.name) + "<br><br>" + icons;
leafletnearbylayer.addData(item);
});
leafletmap.addLayer(leafletnearbylayer);
$(".leaflet-marker-icon").addClass("card-prevent-open");
}
});
}

@ -1,117 +0,0 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
$("#typecodebtn").on("click", function () {
app.dialog.prompt('Enter the recipient\'s code', 'Send Money', function (code) {
if (code != "") {
app.preloader.show();
callAPI("getprofile", {
key: localStorage.getItem("key"),
id: code
}, function (data) {
$("#publicid").val(code);
loadSendMoneyPage();
}, function (msg) {
app.preloader.hide();
app.dialog.alert(msg, "Error");
});
}
});
});
$("#scanqrcodebtn").on("click", function () {
cordova.plugins.barcodeScanner.scan(
function (result) {
if (!result.cancelled) {
console.log("Barcode: ", result);
if (result.format == "QR_CODE" && result.text.startsWith(SETTINGS['webapp_url'])) {
var url = new URL(result.text);
if (typeof url.searchParams.get("sendto") == "string") {
$("#publicid").val(url.searchParams.get("sendto"));
loadSendMoneyPage();
} else {
app.dialog.alert("Not a valid payment code.", "Scan Error");
}
} else {
app.dialog.alert("Not a valid payment code.", "Scan Error");
}
}
},
function (error) {
app.dialog.alert(error, "Scan Error");
},
{
showTorchButton: true,
prompt: "Scan a code to send money",
resultDisplayDuration: 0,
disableSuccessBeep: true
}
);
});
function sendMoney(id, amount, name) {
if (id == "0") {
return;
}
if (amount <= 0) {
app.dialog.alert("Please specify an amount.", "Error");
} else if (amount > 999.99) {
app.dialog.alert("Please specify an amount less than $999.99.", "Error");
}
app.preloader.show();
callAPI("sendmoney", {
key: localStorage.getItem("key"),
to: id,
amount: amount
}, function (data) {
app.preloader.hide();
router.navigate("/moneysent/" + (amount * 1.0).toFixed(2) + "/" + name);
}, function (msg) {
app.preloader.hide();
app.dialog.alert(msg, "Error");
});
}
function loadSendMoneyPage() {
app.preloader.show();
if ($("#publicid").val() == "0") {
app.preloader.hide();
$("#step1").removeClass("display-none");
$("#step2").addClass("display-none");
} else {
$("#step1").addClass("display-none");
$("#step2").removeClass("display-none");
callAPI("getprofile", {
key: localStorage.getItem("key"),
id: $("#publicid").val()
}, function (data) {
app.preloader.hide();
console.log("Profile", data.profile);
$("#person-name").text(data.profile.name);
if (data.profile.bio != "") {
$("#person-bio").text(data.profile.bio);
$("#person-bio").removeClass("display-none");
}
if (data.profile.verified) {
$("#verified-badge").removeClass("display-none");
} else {
$("#unverified-badge").removeClass("display-none");
}
}, function (msg) {
app.preloader.hide();
app.dialog.alert(msg, "Error");
});
}
}
$(".preset-amount-button").click(function () {
$($(this).data("target")).val($(this).data("amount"));
});
$("#sendbtn").click(function () {
sendMoney($("#publicid").val(), $("#amount-box").val(), $("#person-name").text());
});

@ -1,107 +0,0 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
$('#username').on("keyup", function () {
$('#username').val($('#username').val().toLowerCase());
});
function checkAndSave(username, password) {
app.preloader.show();
callAPI("getkey", {
username: username,
password: password
}, function (data) {
app.preloader.hide();
localStorage.setItem("key", data.key);
localStorage.setItem("username", username);
localStorage.setItem("password", password);
callAPI("getprofile", {
key: data.key
}, function (profile) {
if (profile.profile.type == 1) {
localStorage.setItem("accttype", "giver");
} else if (profile.profile.type == 2) {
localStorage.setItem("accttype", "receiver");
} else if (profile.profile.type == 3) {
app.preloader.hide();
app.dialog.alert("You need to download the merchant app instead. Visit ntsm.co/contact if you need help.", "Error");
return;
} else {
app.preloader.hide();
app.dialog.alert("Server replied with unknown account type. You might need to update this app.", "Error");
return;
}
localStorage.setItem("configured", true);
// Restart the app to re-read the config
restartApplication();
}, function (msg) {
app.preloader.hide();
app.dialog.alert(msg, "Error");
});
}, function (msg) {
app.preloader.hide();
app.dialog.alert(msg, "Error");
});
}
function setupAccount() {
var type = $("#accttype").val();
var username = $("#username").val();
var password = $("#password").val();
checkAndSave(username, password, type);
}
function createAccount() {
var type = $("#accttype").val();
var username = $("#username").val();
var password = $("#password").val();
if (username.length < 1) {
app.dialog.alert("Enter a username.", "Error");
return;
}
if (password.length < SETTINGS["min_password_length"]) {
app.dialog.alert("Password must be at least " + SETTINGS["min_password_length"] + " characters long.", "Error");
return;
}
app.dialog.create({
text: "I agree to the terms of service and privacy policy.",
buttons: [
{
text: "No",
onClick: function () {
restartApplication();
}
},
{
text: "Yes",
onClick: function () {
app.preloader.show();
callAPI("signup", {
username: username,
password: password,
accttype: type
}, function (data) {
app.preloader.hide();
checkAndSave(username, password, type);
}, function (msg) {
app.preloader.hide();
app.dialog.alert(msg, "Error");
});
}
},
{
text: "Show",
onClick: function () {
openBrowser(SETTINGS["terms_of_service_url"]);
}
},
]
}).open();
}

@ -1,6 +1,6 @@
{
"name": "com.netsyms.HelpingHelena",
"displayName": "HelpingHelena",
"name": "com.netsyms.HelpingHelenaMerchant",
"displayName": "Helping Helena Merchant",
"version": "1.0.0",
"description": "",
"author": "Netsyms Technologies",

@ -1,67 +0,0 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<div class="page" data-name="addfunds">
<div class="navbar">
<div class="navbar-inner">
<div class="left">
<a href="#" class="link icon-only back">
<i class="icon icon-back"></i>
</a>
</div>
<div class="title">Add Funds</div>
</div>
</div>
<div class="page-content">
<div class="block">
<div class="row justify-content-center">
<div class="col-100 tablet-50 desktop-25">
<div class="card">
<div class="card-content card-content-padding">
<div class="money-input-box">
<div class="currency">$</div>
<input type="tel" min="1.00" max="999.99" placeholder="0.00" id="funds-amount" class="money-input" />
</div>
<p class="segmented segmented-raised segmented-round">
<button class="button button-round preset-amount-button" data-target="#funds-amount" data-amount="10.00">$10</button>
<button class="button button-round preset-amount-button" data-target="#funds-amount" data-amount="20.00">$20</button>
<button class="button button-round preset-amount-button" data-target="#funds-amount" data-amount="50.00">$50</button>
</p>
<div class="margin-vertical padding-top">
<div id="card-element">
<i class="fas fa-spin fa-spinner"></i> Loading secure card form...
</div>
<div id="card-errors" role="alert"></div>
</div>
<p class="text-color-green">
<i class="fas fa-lock"></i> We don't see your card number; it's sent directly and securely to our payment processor.
<p>
<i class="fas fa-info-circle"></i> Your account balance won't gain the entire amount you choose.
<span id="tx-off-percent">7</span>% (or the card fees, whichever is greater) is subtracted to
cover fees and maintenance costs. <a href="" class="link" onclick="addFeesToAmount()">Tap here</a> to add
the fees to your total.
</div>
<div class="card-footer display-block padding-vertical">
<div class="button button-large button-fill button-round button-raised" onclick="chargeCard()">
<i class="fas fa-credit-card"></i> Charge Card
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="https://js.stripe.com/v3/"></script>
<script src="js/addfunds.js"></script>
</div>

@ -1,40 +0,0 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<div class="page" data-name="fundsadded">
<div class="navbar">
<div class="navbar-inner">
<div class="left">
<a href="#" class="link icon-only back">
<i class="icon icon-back"></i>
</a>
</div>
<div class="title">Funds Added</div>
</div>
</div>
<div class="page-content">
<div class="block">
<div class="row justify-content-center">
<div class="col-100 tablet-50 desktop-25">
<div class="card bg-color-green">
<div class="card-content card-content-padding text-align-center text-color-white color-theme-white">
<img style="max-height: 150px;" src="img/bigcheck.svg" alt="Checkmark"/>
<h3>You paid ${{this.$route.params.amount}} total and added ${{this.$route.params.final_amount}} to your balance.</h3>
<a class="button button-outline" href="/home">Close</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

@ -18,167 +18,75 @@
</div>
</div>
<div class="page-content ptr-content" data-ptr-mousewheel="true" data-ptr-distance="20">
<div class="ptr-preloader">
<div class="preloader"></div>
<div class="ptr-arrow"></div>
</div>
<div class="page-content">
<div class="row justify-content-center">
<div class="col-100 tablet-50 desktop-33">
<div class="col-100 tablet-50 desktop-25">
<div class="card">
<div class="card-header">
<div>
<i class="fas fa-wallet"></i> Balance
</div>
<div>
<span class="preloader" id="balance-loading"></span>
<span class="text-color-red" id="balance-error-icon" style="display: none;"><i class="fas fa-exclamation-triangle"></i></span>
<div class="card-content card-content-padding">
<div class="money-input-box">
<div class="currency">$</div>
<input type="tel" min="0.00" max="999.99" placeholder="0.00" id="amount-box" class="money-input" />
</div>
</div>
<div class="card-content card-content-padding text-align-center">
<div id="balance-error"></div>
<h3 class="display" id="balance-label">$<span id="balance-amount">0.00</span></h3>
</div>
{{#if receiver}}
<div class="card-footer justify-content-end">
<a href="/sendmoney/0" class="link">
<i class="fas fa-arrow-up fa-fw"></i> Send Money
</a>
</div>
{{else}}
<div class="card-footer">
<a href="/addfunds" class="link">
<i class="fas fa-credit-card fa-fw"></i> Add Funds
</a>
<a href="/sendmoney/0" class="link">
<i class="fas fa-arrow-up fa-fw"></i> Send Money
</a>
<div class="card-footer display-block padding-vertical">
<div class="button button-large button-fill button-round button-raised" id="showcodebtn">
<i class="fas fa-qrcode"></i> Show Code
</div>
</div>
{{/if}}
</div>
</div>
{{#if receiver}}
<div class="col-100 tablet-50 desktop-33">
<div class="card card-expandable card-expandable-animate-width elevation-2" id="receive-card" style="margin-top: var(--f7-card-margin-vertical);" onclick="openReceiveCard()">
<div class="card-content text-align-center" style="min-height: 16.58em;">
<div class="card-header card-opened-fade-out">
<div>
<i class="fas fa-arrow-down"></i> Receive Money
</div>
<div>
<span class="preloader" id="receive-loading"></span>
</div>
</div>
</div>
<div class="navbar card-opened-fade-in">
<div class="navbar-inner">
<div class="title">Receive Money</div>
</div>
<div class="right">
<a href="#" class="link icon-only card-close">
<i class="material-icons">close</i>
</a>
</div>
</div>
</div>
<div class="popup text-align-center" id="payment-popup">
<div class="navbar">
<div class="navbar-inner">
<div class="title">Pay $<span id="pay-amount"></span></div>
<image id="qrcode" class="text-align-center" src="" />
<h3 id="qrcode-label" class="text-align-center padding-bottom"></h3>
</div>
<div class="right">
<a href="#" class="link icon-only popup-close">
<i class="material-icons">close</i>
</a>
</div>
</div>
</div>
<div class="col-100 tablet-50 desktop-33">
<div class="card" id="broadcast-card">
<image id="qrcode" src="" />
</div>
<div class="card-header">
<div>
<i class="fas fa-street-view"></i> Broadcast Location
</div>
</div>
<div class="popup text-align-center" id="paid-popup">
<div class="navbar">
<div class="navbar-inner">
<div class="title">Thanks!</div>
<div class="card-content card-content-padding">
Press the button to show your current location to nearby
givers. They will be able to send you money from the
map, even if they aren't close enough to scan your code.
Your position will be visible for one hour. The timer
will reset every time you push the button.
</div>
<div class="card-footer display-block padding-vertical">
<div class="button button-large button-fill button-round button-raised" onclick="broadcastLocation()">
Broadcast
</div>
</div>
<div class="right">
<a href="#" class="link icon-only popup-close">
<i class="material-icons">close</i>
</a>
</div>
</div>
{{/if}}
<div class="col-100 tablet-50 desktop-33">
<div class="card card-expandable elevation-2" id="map-card" style="margin-top: var(--f7-card-margin-vertical);">
<div class="card-header card-opened-fade-out" style="z-index: 9999999; background-color: rgba(255,255,255,0.5);">
<div>
{{#if giver}}
<i class="fas fa-search-location"></i> Nearby People
{{else}}
<i class="fas fa-store"></i> Nearby Merchants
{{/if}}
</div>
<div>
<span class="link icon-only card-prevent-open" onclick="recenterMapToUserPosition()" style="margin-top: 0.1em; cursor: pointer;">
<i class="material-icons" style="font-size: 16pt;">my_location</i>
</span>
</div>
</div>
</div>
<div class="card-content text-align-center" style="min-height: 16.58em;">
<div class="navbar card-opened-fade-in">
<div class="navbar-inner">
<div class="title">Nearby {{#if giver}}People{{else}}Merchants{{/if}}</div>
<div class="right">
<a href="" class="link icon-only" onclick="recenterMapToUserPosition()">
<i class="material-icons">my_location</i>
</a>
<a href="#" class="link icon-only card-close">
<i class="material-icons">close</i>
</a>
</div>
</div>
</div>
<div class="block">
<div class="row justify-content-center">
<div class="col-100 tablet-75">
<div class="card bg-color-green">
<div class="card-content card-content-padding text-align-center text-color-white color-theme-white">
<img style="max-height: 150px;" src="img/bigcheck.svg" alt="Checkmark"/>
<div id="map"></div>
<h2>Transaction complete!</h2>
<h3>Total: $<span id="paid-amount"></span></h3>
</div>
</div>
</div>
</div>
</div>
<!-- Make sure the content won't be blocked by the floating button -->
<div id="bottomspacer" style="height: 80px;"></div>
</div>
<div class="fab fab-right-bottom">
<a href="/sendmoney/0">
<div style="width: 100%;">
<img src="img/money-send.svg" style="width: 100%; filter: invert(1);" />
</div>
</a>
</div>
{{#if giver}}
<script src="js/giver_map.js"></script>
{{else}}
<script src="js/receiver_map.js"></script>
{{/if}}
<script src="js/map.js"></script>
<script src="js/home.js"></script>
</div>

@ -2,7 +2,7 @@
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<div class="page" data-name="setup1">
<div class="page" data-name="login">
<div class="navbar">
<div class="navbar-inner">
@ -16,13 +16,12 @@
</div>
<div class="page-content color-theme-{{js "this.$route.params.acctType == 'giver' ? 'green' : 'blue'"}}">
<div class="page-content">
<div class="block">
<div class="row" style="justify-content: center;">
<div class="card col-100 tablet-50">
<div class="card-content card-content-padding">
<input type="hidden" id="accttype" value="{{$route.params.acctType}}">
<div class="list no-hairlines">
<ul>
@ -51,14 +50,9 @@
<br />
<br />
<div class="button button-fill button-raised button-round button-large" onclick="setupAccount()">
<div class="button button-fill button-raised button-round button-large" onclick="doLogin()">
Sign In
</div>
<br />
<div class="button button-outline button-raised button-round button-large" onclick="createAccount()">
Create Account
</div>
</div>
</div>
</div>
@ -67,6 +61,6 @@
</div>
<script src="js/setup1.js"></script>
<script src="js/login.js"></script>
</div>

@ -1,40 +0,0 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<div class="page" data-name="moneysent">
<div class="navbar">
<div class="navbar-inner">
<div class="left">
<a href="#" class="link icon-only back">
<i class="icon icon-back"></i>
</a>
</div>
<div class="title">Money Sent</div>
</div>
</div>
<div class="page-content">
<div class="block">
<div class="row justify-content-center">
<div class="col-100 tablet-50 desktop-25">
<div class="card bg-color-green">
<div class="card-content card-content-padding text-align-center text-color-white color-theme-white">
<img style="max-height: 150px;" src="img/bigcheck.svg" alt="Checkmark"/>
<h3>You sent ${{this.$route.params.amount}} to {{this.$route.params.name}}</h3>
<a class="button button-outline" href="/home">Close</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

@ -1,122 +0,0 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<div class="page" data-name="sendmoney">
<div class="navbar">
<div class="navbar-inner">
<div class="left">
<a href="/home" class="link icon-only back">
<i class="icon icon-back"></i>
</a>
</div>
<div class="title">Send Money</div>
</div>
</div>
<div class="page-content">
<input type="hidden" id="publicid" value="{{this.$route.params.publicID}}" />
<div class="block">
<div id="step1" class="display-none">
<div class="row justify-content-center">
{{#if @global.qrenabled}}
<div class="col-100 tablet-50 desktop-25 margin-bottom margin-horizontal">
<div class="button button-large button-fill button-round" id="scanqrcodebtn">
<i class="fas fa-qrcode"></i> Scan Code
</div>
</div>
{{/if}}
<div class="col-100 tablet-50 desktop-25 margin-horizontal">
<div class="button button-large button-outline button-round" id="typecodebtn">
<i class="fas fa-keyboard"></i> Enter Code
</div>
</div>
</div>
</div>
<div id="step2" class="display-none">
<div class="row justify-content-center">
<div class="col-100 tablet-50 desktop-25">
<div class="card">
<div class="card-content card-content-padding">
<div class="money-input-box">
<div class="currency">$</div>
<input type="tel" min="0.00" max="999.99" placeholder="0.00" id="amount-box" class="money-input" />
</div>
<p class="segmented segmented-raised segmented-round">
<button class="button button-round preset-amount-button" data-target="#amount-box" data-amount="1.00">$1</button>
<button class="button button-round preset-amount-button" data-target="#amount-box" data-amount="5.00">$5</button>
<button class="button button-round preset-amount-button" data-target="#amount-box" data-amount="10.00">$10</button>
<button class="button button-round preset-amount-button" data-target="#amount-box" data-amount="20.00">$20</button>
</p>
</div>
<div class="card-footer display-block padding-vertical">
<div class="button button-large button-fill button-round button-raised" id="sendbtn">
<i class="fas fa-arrow-up"></i> Send
</div>
</div>
</div>
</div>
<div class="col-100 tablet-50 desktop-25">
<div class="card">
<div class="card-content card-content-padding">
<h3 class="no-margin-top"><i class="fas fa-user"></i> <span id="person-name"></span></h3>
<div>
<a id="verified-badge-container" class="link popup-open" href="#" data-popup="#verification-popup">
<span id="verified-badge" class="display-none">
<i class="fas fa-check-circle text-color-green"></i> &nbsp; Verified
</span>
<span id="unverified-badge" class="display-none">
<i class="fas fa-question-circle text-color-blue"></i> &nbsp; Unverified
</span>
</a>
</div>
<div id="person-bio" class="display-none padding-top-half margin-top-half">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="popup" id="verification-popup">
<div class="navbar">
<div class="navbar-inner">
<div class="title">Verified Receivers</div>
<div class="right">
<a href="#" class="link icon-only popup-close">
<i class="material-icons">close</i>
</a>
</div>
</div>
</div>
<div class="block">
<p>
A verified receiver is a person who is confirmed in need of
assistance.
<p>
They have been vetted by a case worker or other person who works
for a local organization that helps people in need.
<p>
Your money will be used for essentials only, regardless of the
receiver's verification status.
</div>
</div>
<script src="js/sendmoney.js"></script>
</div>

@ -1,61 +0,0 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<div class="page" data-name="setup0">
<div class="navbar">
<div class="navbar-inner">
<div class="left">
<a href="#" class="link icon-only back">
<i class="icon icon-back"></i>
</a>
</div>
<div class="title">Welcome</div>
</div>
</div>
<div class="page-content">
<div class="row justify-content-center">
<div class="col-100 desktop-66">
<div class="card">
<div class="card-content card-content-padding text-align-center">
<h3>Choose an account type.</h3>
<p>If you don't have an account, you can create one in the next step.</p>
</div>
</div>
</div>
</div>
<div class="row justify-content-center">
<a class="col-100 tablet-50 desktop-33" href="/setup/1/giver">
<div class="card">
<div class="card-content card-content-padding text-align-center bg-color-green text-color-white">
<span class="fa-stack fa-5x">
<i class="fas fa-money-bill-wave-alt fa-stack-2x"></i>
<i class="fas fa-circle fa-stack-1x"></i>
<i class="fas fa-arrow-alt-circle-up fa-inverse fa-stack-1x text-color-green"></i>
</span>
<h3>Giver</h3>
</div>
</div>
</a>
<a class="col-100 tablet-50 desktop-33" href="/setup/1/receiver">
<div class="card">
<div class="card-content card-content-padding text-align-center bg-color-blue text-color-white">
<span class="fa-stack fa-5x">
<i class="fas fa-money-bill-wave-alt fa-stack-2x"></i>
<i class="fas fa-circle fa-stack-1x"></i>
<i class="fas fa-arrow-alt-circle-down fa-inverse fa-stack-1x text-color-blue"></i>
</span>
<h3>Receiver</h3>
</div>
</div>
</a>
</div>
</div>
</div>

@ -11,7 +11,7 @@
<i class="icon icon-back"></i>
</a>
</div>
<div class="title">My Profile</div>
<div class="title">Business Profile</div>
<div class="right">
<a href="" onclick="saveProfile()" class="link">
<i class="fas fa-cloud-upload-alt"></i> <span>Save</span>
@ -31,7 +31,7 @@
<div class="item-inner">
<div class="item-title item-label">Name</div>
<div class="item-input-wrap">
<input type="text" id="name" placeholder="Your name" />
<input type="text" id="name" placeholder="Business name" />
<span class="input-clear-button"></span>
</div>
</div>
@ -40,7 +40,7 @@
<div class="item-inner">
<div class="item-title item-label">About</div>
<div class="item-input-wrap">
<textarea id="bio" placeholder="Tell people about yourself" class="resizable"></textarea>
<textarea id="bio" placeholder="Short description of the business" class="resizable"></textarea>
<span class="input-clear-button"></span>
</div>
</div>

@ -10,13 +10,6 @@ var routes = [
path: '/home',
templateUrl: './pages/home.html',
name: 'home',
keepAlive: true,
options: {
context: {
giver: localStorage.getItem("accttype") == "giver",
receiver: localStorage.getItem("accttype") == "receiver"
}
},
on: {
pageAfterIn: function () {
function tryToLoadHomePage() {
@ -31,58 +24,9 @@ var routes = [
}
},
{
path: '/setup/0',
url: './pages/setup0.html',
name: 'setup0'
},
{
path: '/setup/1/:acctType',
templateUrl: './pages/setup1.html',
name: 'setup1'
},
{
path: '/sendmoney/:publicID',
templateUrl: './pages/sendmoney.html',
name: 'sendmoney',
on: {
pageAfterIn: function () {
function tryToLoadSendMoneyPage() {
if (typeof loadSendMoneyPage != "function") {
setTimeout(tryToLoadSendMoneyPage, 500);
} else {
loadSendMoneyPage();
}
}
tryToLoadSendMoneyPage();
}
}
},
{
path: '/moneysent/:amount/:name',
templateUrl: './pages/moneysent.html',
name: 'moneysent'
},
{
path: '/addfunds',
templateUrl: './pages/addfunds.html',
name: 'addfunds',
on: {
pageAfterIn: function () {
function tryToInitPaymentPage() {
if (typeof initPaymentPage != "function") {
setTimeout(tryToInitPaymentPage, 500);
} else {
initPaymentPage();
}
}
tryToInitPaymentPage();
}
}
},
{
path: '/fundsadded/:amount/:final_amount',
templateUrl: './pages/fundsadded.html',
name: 'fundsadded'
path: '/login',
url: './pages/login.html',
name: 'login'
},
{
path: '/updateprofile',
@ -126,18 +70,18 @@ var routes = [
{
setting: "updateprofile",
title: "Update profile",
text: "Set a name and tell people about you.",
text: "Change the business name and information.",
onclick: "router.navigate('/updateprofile')"
},
{
setting: "account",
setting: "logout",
title: "Sign out",
onclick: "router.navigate('/setup/0')"
onclick: "router.navigate('/login')"
},
{
setting: "versions",
title: "Helping Helena app v1.0.0",
text: "Copyright &copy; 2018-2019 Netsyms Technologies. License: <span style=\"text-decoration: underline;\" onclick=\"openBrowser('https://source.netsyms.com/Netsyms/HelpingHelenaApp?pk_campaign=HelpingHelenaApp');\">Mozilla Public License 2.0</span>.",
title: "Helping Helena Merchant v1.0.0",
text: "Copyright &copy; 2018-2019 Netsyms Technologies. License: <span style=\"text-decoration: underline;\" onclick=\"openBrowser('https://source.netsyms.com/Netsyms/HelpingHelenaMerchant?pk_campaign=HelpingHelenaMerchantApp');\">Mozilla Public License 2.0</span>.",
onclick: ""
},
{

@ -10,9 +10,7 @@
var SETTINGS = {
server: "http://localhost/helpinghelena/api",
webapp_url: "https://app.helpinghelena.org/",
terms_of_service_url: "https://netsyms.com/legal?pk_campaign=HelpingHelenaApp",
terms_of_service_url: "https://netsyms.com/legal?pk_campaign=HelpingHelenaMerchantApp",
min_password_length: 8,
map_style_json: "https://maps.netsyms.net/styles/klokantech-3d-gl/style.json",
stripe_pubkey: "",
addfunds_percent: 0.07
};
map_style_json: "https://maps.netsyms.net/styles/klokantech-3d-gl/style.json"
};

Loading…
Cancel
Save