diff --git a/composer.json b/composer.json index 5909527..ea90c45 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,8 @@ "type": "project", "require": { "catfan/medoo": "^1.5", - "guzzlehttp/guzzle": "^6.2" + "guzzlehttp/guzzle": "^6.2", + "stripe/stripe-php": "^6.24" }, "license": "MPL-2.0", "authors": [ diff --git a/composer.lock b/composer.lock index 8d36028..5535e42 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "5c7439c6e041764f2f6b0270a95ab3ae", - "content-hash": "e4e700119f47d2f68b0ed82abaf8c5c6", + "content-hash": "87c8c709e248bfe62291e8208765fd01", "packages": [ { "name": "catfan/medoo", @@ -64,7 +63,7 @@ "sql", "sqlite" ], - "time": "2018-06-14 18:59:08" + "time": "2018-06-14T18:59:08+00:00" }, { "name": "guzzlehttp/guzzle", @@ -129,7 +128,7 @@ "rest", "web service" ], - "time": "2018-04-22 15:46:56" + "time": "2018-04-22T15:46:56+00:00" }, { "name": "guzzlehttp/promises", @@ -180,7 +179,7 @@ "keywords": [ "promise" ], - "time": "2016-12-20 10:07:11" + "time": "2016-12-20T10:07:11+00:00" }, { "name": "guzzlehttp/psr7", @@ -245,7 +244,7 @@ "uri", "url" ], - "time": "2017-03-20 17:10:46" + "time": "2017-03-20T17:10:46+00:00" }, { "name": "psr/http-message", @@ -295,7 +294,62 @@ "request", "response" ], - "time": "2016-08-06 14:39:51" + "time": "2016-08-06T14:39:51+00:00" + }, + { + "name": "stripe/stripe-php", + "version": "v6.24.0", + "source": { + "type": "git", + "url": "https://github.com/stripe/stripe-php.git", + "reference": "e608b6538b45d233db66838c389c6548c1152a27" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/stripe/stripe-php/zipball/e608b6538b45d233db66838c389c6548c1152a27", + "reference": "e608b6538b45d233db66838c389c6548c1152a27", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "php-coveralls/php-coveralls": "1.*", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "Stripe\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Stripe and contributors", + "homepage": "https://github.com/stripe/stripe-php/contributors" + } + ], + "description": "Stripe PHP Library", + "homepage": "https://stripe.com/", + "keywords": [ + "api", + "payment processing", + "stripe" + ], + "time": "2018-11-28T16:32:29+00:00" } ], "packages-dev": [], diff --git a/database.mwb b/database.mwb index df06dfb..a54c587 100644 Binary files a/database.mwb and b/database.mwb differ diff --git a/public/actions/submitmembership.php b/public/actions/submitmembership.php index 9a80716..24ffe1f 100644 --- a/public/actions/submitmembership.php +++ b/public/actions/submitmembership.php @@ -6,4 +6,177 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -var_export($_POST); \ No newline at end of file +require_once __DIR__ . "/../../lib/requiredpublic.php"; + +function errorBack(string $errormsg) { + header("Location: ../?error=" . htmlentities($errormsg)); + die($errormsg); +} + +if (empty($_POST['agree_terms'])) { + errorBack("You must agree to HACHE's policy."); +} + +$database->action(function($database) { + $lastname = $_POST['familyname']; + $father = $_POST['fathername']; + $mother = $_POST['mothername']; + + if (empty($lastname)) { + errorBack("Enter a last name."); + } + if (empty($father)) { + errorBack("Enter a father name."); + } + if (empty($mother)) { + errorBack("Enter a mother name."); + } + + $phone = $_POST['phone']; + $phone = preg_replace("/[^0-9]/", "", $phone); + if (strlen($phone) == 11) { + $phone = preg_replace("/^1/", "", $phone); + } + if (strlen($phone) != 10) { + errorBack("Enter a valid 10-digit phone number."); + } + + $email = $_POST['email']; + if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { + errorBack("The email address looks wrong."); + } + + $address = $_POST['streetaddress']; + $city = $_POST['city']; + $state = strtoupper($_POST['state']); + $zip = $_POST['zip']; + if (empty($address)) { + errorBack("Enter a street address."); + } + if (empty($city)) { + errorBack("Enter a city."); + } + if (!preg_match("/^[A-Z]{2}$/", $state)) { + errorBack("Enter a valid two-character state (MT, WY, ID, etc)."); + } + if (!preg_match("/^[0-9]{5}(-?[0-9]{4})?$/", $zip)) { + errorBack("Enter a valid five or nine digit US ZIP code."); + } + + $newsletter = $_POST['newsletter_method']; + $membership_cost = 2500; + if (empty($newsletter)) { + errorBack("Select a newsletter preference."); + } + switch ($newsletter) { + case 1: // Email only + $membership_cost = 2500; + break; + case 2: // Print only + $membership_cost = 3500; + break; + case 3: // Email and print + $membership_cost = 3500; + break; + default: + errorBack("Select a valid newsletter preference."); + } + + $photopermission = $_POST['photo_permission']; + if (!empty($photopermission) && $photopermission == "1") { + $photopermission = true; + } else { + $photopermission = false; + } + + $database->insert("families", [ + "familyname" => $lastname, + "father_name" => $father, + "mother_name" => $mother, + "phone" => $phone, + "email" => $email, + "newsletter_method" => $newsletter, + "address" => $address, + "city" => $city, + "state" => $state, + "zip" => $zip, + "photo_permission" => $photopermission + ]); + + $familyid = $database->id(); + + $children = $_POST['child']; + + foreach ($children['ids'] as $cid) { + if (empty($children['name'][$cid])) { + continue; + } + + if (!preg_match("/^([1-9]|1[012])$/", $children['month'][$cid])) { + errorBack("Invalid birth month chosen for " . htmlentities($children['name'][$cid]) . "."); + } + + if (!is_numeric($children['year'][$cid])) { + errorBack("Invalid birth year chosen for " . htmlentities($children['name'][$cid]) . "."); + } + $children['year'][$cid] = $children['year'][$cid] * 1; + if ($children['year'][$cid] < 1980 || $children['year'][$cid] > date("Y")) { + errorBack("Invalid birth year chosen for " . htmlentities($children['name'][$cid]) . "."); + } + + $database->insert("people", [ + "familyid" => $familyid, + "name" => $children['name'][$cid], + "birthday" => $children['year'][$cid] . "-" . $children['month'][$cid] . "-00", + "graduated" => empty($children['graduate'][$cid]) ? 0 : 1 + ]); + } + + $interests = []; + foreach ($_POST['events'] as $evt) { + if ($database->has("events", ['eventid' => $evt])) { + $interests[] = ["familyid" => $familyid, "eventid" => $evt]; + } + } + $database->insert("interests", $interests); + + + try { + \Stripe\Stripe::setApiKey(STRIPE_SECKEY); + + $charge = \Stripe\Charge::create([ + 'amount' => $membership_cost, + 'currency' => 'usd', + 'description' => 'HACHE Membership', + 'source' => $_POST['stripeToken'], + 'statement_descriptor' => 'HACHE Membership 1yr', + ]); + + } catch (\Stripe\Error\Card $e) { + $body = $e->getJsonBody(); + $err = $body['error']; + errorBack("We couldn't process your card because it was declined. Your card issuer or bank sent us this message: " . $err["message"] . " That's all we know."); + } catch (\Stripe\Error\RateLimit $e) { + errorBack("We couldn't process your card because things are happening too fast. Please try again in a minute. (Error code: STRIPE_RATELIMIT)"); + } catch (\Stripe\Error\InvalidRequest $e) { + errorBack("We couldn't process your card because of a technical issue. Please try again later. (Error code: STRIPE_INVREQ)"); + } catch (\Stripe\Error\Authentication $e) { + errorBack("We can't connect to the card processor. Please try again later. (Error code: STRIPE_AUTH)"); + } catch (\Stripe\Error\ApiConnection $e) { + errorBack("We can't connect to the card processor. Please try again later. (Error code: STRIPE_NOAPI)"); + } catch (\Stripe\Error\Base $e) { + errorBack("An unknown payment error occurred. Please try again later."); + } catch (Exception $e) { + errorBack("An unknown error occurred. Please try again later."); + } + + $database->insert("payments", [ + "familyid" => $familyid, + "amount" => ($membership_cost / 100.0), + "paid" => 1, + "date" => date("Y-m-d H:i:s") + ]); + + header("Location: ../?page=thanks"); + return true; +}); diff --git a/public/index.php b/public/index.php index 7adba6f..3250e6a 100644 --- a/public/index.php +++ b/public/index.php @@ -10,8 +10,8 @@ require_once __DIR__ . "/../lib/requiredpublic.php"; $page = "signup.php"; if (!empty($_GET['page'])) { switch ($_GET['page']) { - case "pay": - $page = "pay.php"; + case "thanks": + $page = "thanks.php"; } } ?> diff --git a/public/parts/pay.php b/public/parts/pay.php deleted file mode 100644 index a9f1bc9..0000000 --- a/public/parts/pay.php +++ /dev/null @@ -1,10 +0,0 @@ - -
- -
\ No newline at end of file diff --git a/public/parts/signup.php b/public/parts/signup.php index 848f869..4cda1ce 100644 --- a/public/parts/signup.php +++ b/public/parts/signup.php @@ -6,7 +6,7 @@ */ ?>
-
+
@@ -19,6 +19,18 @@
+ +
+
+ +
+
+ +

Basic Information

@@ -72,7 +84,7 @@ "label" => "ZIP/Postal Code", "icon" => "fas fa-mail-bulk", "name" => "zip", - "maxlength" => 20, + "maxlength" => 10, "width" => 3 ], [ @@ -305,6 +317,19 @@
+
+

Pay and Submit

+
+
+ +
+ +
+
+ +
@@ -312,4 +337,8 @@
+ + \ No newline at end of file diff --git a/public/parts/thanks.php b/public/parts/thanks.php new file mode 100644 index 0000000..0d2d341 --- /dev/null +++ b/public/parts/thanks.php @@ -0,0 +1,23 @@ + +
+
+
+ +
+ HACHE: Helena Area Christian Home Educators + +

Thank You!

+ + Checkmark + +

Your membership has been submitted and paid for. We'll be in touch soon!

+
+
+
+
\ No newline at end of file diff --git a/public/static/bigcheck.svg b/public/static/bigcheck.svg new file mode 100644 index 0000000..5b6c4ba --- /dev/null +++ b/public/static/bigcheck.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/public/static/signup.js b/public/static/signup.js index 9cf94b5..833edda 100644 --- a/public/static/signup.js +++ b/public/static/signup.js @@ -7,6 +7,44 @@ $("#add_child_row").click(function () { $.get("parts/template_child_entry.php", {}, function (resp) { - $("#child_list").append(resp); + $("#child_list").append(resp); + }); +}); + +// Create a Stripe client. +var stripe = Stripe(stripe_pubkey); + +// Create an instance of Elements. +var elements = stripe.elements(); + +// Create an instance of the card Element. +var card = elements.create('card'); + +// Add an instance of the card Element into the `card-element`
. +card.mount('#card-element'); + +card.addEventListener('change', function (event) { + if (event.error) { + $("#card-errors").removeClass("d-none"); + $("#card-errors").text(event.error.message); + } else { + $("#card-errors").addClass("d-none"); + $("#card-errors").text(""); + } +}); + +$("#membershipform").on("submit", function (event) { + event.preventDefault(); + + stripe.createToken(card).then(function (result) { + if (result.error) { + // Inform the customer that there was an error. + $("#card-errors").removeClass("d-none"); + $("#card-errors").text(event.error.message); + } else { + $("#stripe-token").val(result.token.id); + console.log(result.token); + document.getElementById('membershipform').submit(); + } }); }); \ No newline at end of file diff --git a/settings.template.php b/settings.template.php index 8eb971d..b17173c 100644 --- a/settings.template.php +++ b/settings.template.php @@ -20,6 +20,8 @@ define("DB_CHARSET", "utf8"); // Name of the app. define("SITE_TITLE", "Membership Portal"); +define("STRIPE_PUBKEY", ""); +define("STRIPE_SECKEY", ""); // URL of the AccountHub API endpoint define("PORTAL_API", "http://localhost/accounthub/api.php");