diff --git a/conf/general-example b/conf/general-example index 8adf28b4a2..b014bb3867 100644 --- a/conf/general-example +++ b/conf/general-example @@ -183,3 +183,8 @@ define('REDIS_SENTINELS', '127.0.0.1'); # Can be comma separated list define('REDIS_SENTINEL_PORT', '26379'); define('REDIS_SERVICE_NAME', 'mymaster'); +// donate settings +define('OPTION_STRIPE_PUBLISHABLE_API_KEY', ''); +define('OPTION_STRIPE_SECRET_API_KEY', ''); +define('OPTION_RECAPTCHA_SITE_KEY', ''); +define('OPTION_RECAPTCHA_SECRET', ''); diff --git a/www/docs/js/main.js b/www/docs/js/main.js index dd7dbea899..aa4fae2e3e 100644 --- a/www/docs/js/main.js +++ b/www/docs/js/main.js @@ -358,3 +358,128 @@ function trackLinkClick(link, category, name, value) { document.location.href = link.href; }) } + +/* Donate page */ + +$(function() { + + if ($.easytabs){ + $('#tab-container').easytabs({ animate: false }); + } + + var selected = {}; + + $('#how-often-monthly').click(function() { + $('.donate-recurring-amount').show(); + $('.donate-one-off-amount').hide(); + $('#recurring-how-much-10').prop('checked', true); + }); + $('#how-often-once').click(function() { + $('.donate-recurring-amount').hide(); + $('.donate-one-off-amount').show(); + $('#one-off-how-much-50').prop('checked', true); + + }); + + $('#gift-aid-yes').click(function() { + if($(this).is(':checked')) { + $('.donate-fullname').show(); + } else { + $('.donate-fullname').hide(); + } + }); + + //Donate form 'other' amount box toggle if box value is empty + current_value = $('#how-much-other-value__input').val(); + if (current_value == '') { + $('#how-much-other-value__input').prop( "disabled", true ); + $('.how-much-other-value').hide(); + }; + + $('[id^=how-much-]').click(function(){ + if($('#how-much-other').is(":checked")){ + $('.how-much-other-value').slideDown(100, function(){ + $('.how-much-other-value__input').prop( "disabled", false ).focus(); + }); + } + else if($('#how-much-other').is(":not(:checked)")){ + $('.how-much-other-value').slideUp(100, function(){ + $('.how-much-other-value__input').prop( "disabled", true ); + }); + } + }); + + $('input[type=radio]').click(function() { + selected[this.name] = 1; + var total = 0; + for (s in selected) { + total++; + } + if (total === 4) { + $('.form__error').remove(); + } + }); + + $('#donate_button').click(function(e) { + e.preventDefault(); + var giftaid = $('input[name=gift-aid]:checked').val(); + var howoften = $('input[name=how-often]:checked').val(); + var amount = $('input[name=how-much]:checked').val(); + var contact_permission = $('input[name=contact_permission]:checked').val(); + var full_name = $('input[name=full_name]').val(); + + if (amount == 'other') { + amount = $('input[name=how-much-other]').val(); + } + $('.form__error').remove(); + if (!amount || !howoften) { + $(this).parent().before('
Please select an amount to donate.
'); + return; + } + if (!contact_permission) { + $(this).parent().before('Please tell us if we can contact you about our work (or not!).
'); + return; + } + if (giftaid == 'Yes' && !full_name) { + $(this).parent().before('Please enter your full name for gift aid.
'); + return; + } + + var submitPaymentForm = function(){ + grecaptcha.execute(); + }; + + if (!window.analytics) { + return submitPaymentForm(); + } + + window.analytics.trackEvent( + "donate_form_submit", {"frequency": howoften, "value": amount } + ).done(submitPaymentForm); + }); + + }); + + function onDonateError(message) { + var displayError = document.getElementsByClassName('form__error-wrapper')[0]; + document.getElementById('spinner').style.display = 'none'; + displayError.innerHTML = '' + message + '
'; + } + + function onDonatePass(token) { + var data = $(document.donation_form).serialize(); + document.getElementById('spinner').style.display = 'inline-block'; + $.post('/support-us/?stripe=1', data, 'json').then(function(result) { + if (result.error) { + return onDonateError(result.error); + } + stripe.redirectToCheckout({ + sessionId: result.id + }).then(function(result) { + if (result.error) { + onDonateError(result.error.message); + } + }); + }); + } + \ No newline at end of file diff --git a/www/docs/style/sass/_twfy-mixins.scss b/www/docs/style/sass/_twfy-mixins.scss index 211a1ccf00..05eb865dd3 100644 --- a/www/docs/style/sass/_twfy-mixins.scss +++ b/www/docs/style/sass/_twfy-mixins.scss @@ -228,14 +228,6 @@ $weight_bold: 700; } } -.donation-box { - border: 2px solid black; - border-radius: 10px; - padding: 10px; - text-align:center; - background-color: white; - -} .site-nav a.donate-button { background-color: darken($colour_pale_red, 10%); diff --git a/www/docs/style/sass/app.scss b/www/docs/style/sass/app.scss index 7b18ebfe21..3484e5c59f 100644 --- a/www/docs/style/sass/app.scss +++ b/www/docs/style/sass/app.scss @@ -266,5 +266,6 @@ form { @import "parts/policies"; @import "parts/regional-headers"; @import "parts/toc"; +@import "parts/donate_form"; @import "print"; diff --git a/www/docs/style/sass/parts/_donate_form.scss b/www/docs/style/sass/parts/_donate_form.scss new file mode 100644 index 0000000000..5256a20c49 --- /dev/null +++ b/www/docs/style/sass/parts/_donate_form.scss @@ -0,0 +1,515 @@ +// form scss generally copied from the mySociety.org stylesheets + +.donation-box { + border: 2px solid black; + border-radius: 10px; + padding: 10px; + background-color: white; + +} + +@mixin grid-column--full { + @include grid-column; + width: 100%; +} + +@mixin grid-column--one-third--no-gutter { + @include grid-column--no-gutter; + width: 25%; +} + +@mixin grid-column--no-gutter { + float: left; +} + + +$size-font-base: 18px; +$donate-form-breakpoint: 758px; + +$colour_orange: #E0A55A; +$colour_blue: #48A9F0; +$colour_green: #77BB63; +$colour_yellow: #FEDC66; +$colour_red: #DA545D; +$colour_purple: #B260B0; +$colour_off_white: #F5F3ED; +$colour_lighter_grey: #F0EDE6; +$colour_light_grey: #DFDED8; +$colour_grey: #C6C4BE; +$colour_mid_grey: #959287; +$colour_dark_grey: #615E58; +$colour_black: #424242; + +$colour_background: $colour_black; +$colour_links: $colour_blue; +$colour_borders: $colour_light_grey; + + +@mixin flexbox() { + display: -webkit-box; + display: -moz-box; + display: -ms-flexbox; + display: -webkit-flex; + display: flex; + } + +@mixin flex-align($alignment) { + -webkit-box-align: $alignment; + -webkit-align-items: $alignment; + -ms-flex-align: $alignment; + align-items: $alignment; + } + +@function calculateRem($size, $base) { + $remSize: $size / $base; + @return #{$remSize}rem; + } + + /* + * Send a size in pixels, property (optional), and a base size (optional) + * Returns the same size calculated in rems and a fall-back px for older browsers + * Input + * @include remSize(18px, margin-top, 16px); + * Output + * margin-top: 18px; + * margin-top: 1.125rem; + */ + @mixin remSize($size, $property:font-size, $base:$size-font-base) { + #{$property}: $size; //Fallback in px + #{$property}: calculateRem($size, $base); + } + + + .donate-form { + + .form__element { + max-width: 36em; + } + } + + .donate-page-header { + padding-bottom: 1.5em; + position: relative; + } + + .donate-page-header__title { + @include remSize(36px); + } + + .donate-page-header__para { + @include remSize(21px); + @include remSize(27px, line-height); + color: $colour_dark_grey; + } + + .donation-thanks__message { + z-index: 3; + position: relative; + } + + .donation-thanks__message__header { + text-align: center; + @include remSize(60px); + font-weight: bold; + + @media (min-width: $medium-screen) { + @include remSize(96px); + } + } + + .donation-thanks__message__para { + @include remSize(21px); + line-height: 1.5em; + color: $colour_dark_grey; + } + + .donation-thanks__message__share { + text-align: center; + } + + .donate-canvas { + margin-bottom: 4em; + @media (min-width: $medium-screen) { + margin-bottom: 8em; + } + } + + .confetti { + background-image: url('../img/confetti.png'); + background-repeat: no-repeat; + background-position: center center; + background-size: 477px 370px; + height: 741px; + width: 100%; + max-width: 100%; + position: absolute; + top: -230px; + left: 0; + z-index: 1; + + @media (min-width: $medium-screen) { + top: -100px; + background-size: 954px 741px; + } + + @media ($high-dpi-screen) { + background-image: url('../img/confetti@2.png'); + } + } + + .donate-page-sidebar { + @include grid-column--full; + @media (min-width: $donate-form-breakpoint) { + @include grid-column--one-third--no-gutter; + } + } + + .donate-page-sidebar__entry { + background-color: $colour_off_white; + @include remSize(21px, padding); + border-radius: 5px; + @media (min-width: $medium-screen) { + width: 120%; + margin-right: -20%; + } + } + + .donate-page-sidebar__title { + @include remSize(13px); + text-transform: uppercase; + font-weight: bold; + margin-bottom: 0.5em; + color: $colour_dark_grey; + } + + .donate-page-sidebar__name { + @include remSize(23px); + margin-bottom: 0.25em; + } + + .donate-page-sidebar__content { + @include remSize(14px); + } + + .donate-page-sidebar__image { + margin-bottom: 1em; + border-radius: 50%; + border: 10px solid #fff; + } + + .donate-fullname, + .how-much-other-value { + background-color: $colour_off_white; + padding: 1em; + width: 100%; + border: 1px solid $colour_borders; + border-radius: 3px; + } + + .mysoc-spinner--small { + font-size: 0.5em; + display: none; + margin-left: 1em; + } + + .form__element--submit { + display: flex; + align-items: center; + } + + + + + + .form__element--vertically-stacked { + label, + input { + display: block; + } + } + + .form__element__heading { + display: block; + font-weight: 600; + @include remSize(21px); + margin-bottom: 1rem; + line-height: 1.1em; + } + + .form__element__explainer { + display: block; + @include remSize(14px); + color: $colour_dark_grey; + line-height: 1.35em; + } + + .inline-radio-label { + // Radio button with a label to the right + // [ ] Label + cursor: pointer; + display: inline-block; + + @media (min-width: $donate-form-breakpoint) { + margin-right: 1em; + + &:last-of-type { + margin-right: 0; + } + } + + input[type="radio"] { + margin-right: 0.5em; + margin-bottom: 0; + cursor: pointer; + } + + .form__element__explainer { + margin-left: 23px; + } + } + + .fat-radio-buttons { + @include flexbox; + flex-flow: row wrap; + + .form__element__heading { + flex: 0 0 100%; + } + + .inline-radio-label { + @include flexbox; + @include flex-align(center); + @include justify-content(center); + border: 1px solid $colour_light_grey; + border-bottom: 0; + padding: 1em 1.65em 1em 1.35em; + width: 100%; + + &:first-of-type { + border-radius: 3px 3px 0 0; + } + + &:last-of-type { + border-bottom: 1px solid $colour_light_grey; + border-radius: 0 0 3px 3px; + } + + @media(min-width: $donate-form-breakpoint) { + flex: 0 1 auto; + width: auto; + border-bottom: 1px solid $colour_light_grey; + border-radius: 3px; + margin-bottom: 1em; + + &:first-of-type, + &:last-of-type { + border-radius: 3px; + } + } + + &:hover, + &:active, + &:focus { + border-color: #bbb; + } + } + + .radio-button-with-title { + padding: 0; + position: relative; + + @media (min-width: $donate-form-breakpoint) { + flex-basis: 0; + flex-grow: 1; + } + + input[type="radio"] { + position: absolute; + left: 1em; + top: 1em; + } + } + + .radio-button-with-title__heading { + background-color: $colour_off_white; + border-bottom: 1px solid $colour_light_grey; + display: block; + padding: 0.5em 1em 0.5em 1.7em; + @include remSize(27px); + border-radius: 3px 3px 0 0; + font-weight: 600; + } + + .radio-button-with-title__para { + display: inline-block; + padding: 1em 1.5em 1em 1em; + @include remSize(14px); + color: $colour_dark_grey + } + } + + .fat-radio-buttons__form-inside { + padding: 0.777777778em 1em; + + label { + @include remSize(14px); + } + + input:disabled { + opacity: 0.5; + background-color: $colour_off_white; + } + } + + .radio-label-large { + @include remSize(32px); + font-weight: 600; + } + + .donate-amounts { + justify-content: flex-start; + margin-top: 0em; + } + + .how-much-other-value { + position: relative; + margin-top: 2em; + @media (min-width: $donate-form-breakpoint) { + margin-top: 0; + } + } + + input.how-much-other-value__input { + padding-left: 1.25em; + max-width: 100%; + width: 8em; + z-index: 1; + } + + .how-much-other-value__currency { + position: absolute; + left: 1.5em; + top: 2.0em; + z-index: 5; + color: $colour_dark_grey; + } + + .donate-giftaid, + .donate-contact { + max-width: 36em; + padding-top: 10px; + } + + + .donate-fullname { + display: none; + } + + .donate-giftaid__label, + .donate-contact__label { + display: block; + position: relative; + cursor: pointer; + border: 1px solid $colour_light_grey; + padding: 1em 1.7em 1em 3em; + + &:first-of-type { + border-top-left-radius: 3px; + border-top-right-radius: 3px; + } + + &:last-of-type { + border-bottom-left-radius: 3px; + border-bottom-right-radius: 3px; + } + + & + & { + margin-top: -1px; + } + + &:hover, + &:active, + &:focus { + border-color: #bbb; + z-index: 1; + } + + input { + position: absolute; + top: 1.35em; + left: 1.35em; + } + + small { + display: block; + font-size: 0.8em; + line-height: 1.4em; + margin-top: 0.5em; + color: #777; + } + } + + .form__element__input[type] { + padding: 0.75em 1em; + width: 20em; + max-width: 100%; + } + + .form__element__explainer--cards { + @extend .image-replacement; + margin-top: 0.5em; + width: 147px; + height: 20px; + background-image: url('../img/payment-options-cards.png'); + background-size: 147px 20px; + background-repeat: no-repeat; + + @media ($high-dpi-screen) { + background-image: url('../img/payment-options-cards@2.png'); + } + } + + .form__element__explainer--dd { + @extend .image-replacement; + margin-top: 0.5em; + width: 100px; + height: 32px; + background-image: url('../img/direct-debit.png'); + background-size: 100px 32px; + background-repeat: no-repeat; + + @media ($high-dpi-screen) { + background-image: url('../img/direct-debit@2.png'); + } + } + + .paypal-logo { + display: inline-block; + @extend .image-replacement; + margin-top: 0.5em; + width: 108px; + height: 29px; + background-image: url('../img/paypal-logo.png'); + background-size: 108px 29px; + background-repeat: no-repeat; + margin-top: 0; + + @media ($high-dpi-screen) { + background-image: url('../img/paypal-logo@2.png'); + } + } + + .paypal-logo-radio { + top: -12px !important; //makes the radio button align with the others + } + + .form__error { + background-color: $colour_red; + color: #fff; + padding: 1.25em 1.5em; + border-radius: 5px; + position: relative; + z-index: 10; + box-shadow: 0px 2px 2px 0 rgba(0,0,0,0.2); + } + \ No newline at end of file diff --git a/www/docs/support-us/index.php b/www/docs/support-us/index.php index 68dc623420..08ae5a6f8e 100644 --- a/www/docs/support-us/index.php +++ b/www/docs/support-us/index.php @@ -1,5 +1,10 @@ array( + 'header' => $headers, + 'method' => 'POST', + 'content' => $data, + 'ignore_errors' => TRUE, + ) + ); + $context = stream_context_create($options); + $result = file_get_contents($url, false, $context); + $result = json_decode($result); + return $result; +} + +function donate_amount() { + $amount = $_POST['how-much']; + if ($amount == 'other') { + $amount = $_POST['how-much-other']; + } + return $amount; +} + +function giftaid_decision() { + if (isset($_POST['gift-aid'])) { + return 'Yes'; + } else { + return 'No'; + } +} + +function contact_decision() { + return isset($_POST['contact_permission']) ? $_POST['contact_permission'] : 'No'; +} + +function donate_success() { + header('Location: https://www.mysociety.org/donate/donation-thanks/'); + exit; +} + +# Stripe + +function stripe_post($path, $data) { + $result = donate_post_api( + OPTION_STRIPE_SECRET_API_KEY, 'https://api.stripe.com/v1' . $path, $data, 'html' + ); + if (isset($result->error)) { + return "Sorry, there was an error processing your donation: {$result->error->message}. Please try again."; + } + return $result; +} + +function stripe_session_ajax() { + $amount = donate_amount(); + $giftaid = giftaid_decision(); + $howoften = $_POST['how-often']; + $utm_source = $_POST['utm_source']; + $utm_content = $_POST['utm_content']; + $utm_medium = $_POST['utm_medium']; + $utm_campaign = $_POST['utm_campaign']; + $full_name = $_POST['full_name']; + $contact_permission = contact_decision(); + + $metadata = array( + 'gift-aid' => $giftaid, + 'gift-aid-name' => $full_name, + 'utm_source' => $utm_source, + 'utm_content' => $utm_content, + 'utm_medium' => $utm_medium, + 'utm_campaign' => $utm_campaign, + 'contact_permission' => $contact_permission, + ); + // set billing addres var to required if giftaid is true + if ($giftaid == 'Yes') { + $collectBilling = 'required'; + $name = "Donation to mySociety (with gift aid)"; + } else { + $collectBilling = 'auto'; + $name = "Donation to mySociety"; + } + $data = array( + 'payment_method_types' => ['card' ,'bacs_debit', 'paypal'], + 'success_url' => 'https://www.mysociety.org/donate/donation-thanks/', + 'cancel_url' => 'https://www.mysociety.org/donate/donation-cancelled/', + 'billing_address_collection' => $collectBilling, + ); + + $mysocDesc = " + mySociety is a charity committed to making a fairer society + by providing digital services, research and data, openly and + at no cost. We use technology to help people understand and + take part in the decisions that affect their lives and communities. + We run services such as TheyWorkForYou, WhatDoTheyKnow and FixMyStreet. + "; + + if ($howoften == 'monthly') { + $data['subscription_data'] = array( + 'items' => [ [ + 'plan' => 'donate', + 'quantity' => $amount, + ] ], + 'metadata' => $metadata, + 'description' => $mysocDesc, + ); + } elseif ($howoften == 'once') { + $data += array( + 'payment_intent_data' => array( + 'metadata' => $metadata, + ), + 'submit_type' => 'donate', + 'line_items' => [ array( + 'amount' => $amount * 100, + 'currency' => 'gbp', + 'name' => $name, + 'description' => $mysocDesc, + 'quantity' => 1, + ) ], + ); + } + $result = stripe_post('/checkout/sessions', $data); + if (is_string($result)) { + return array('error' => $result); + } else { + return $result; + } +} + + +function verify_recaptcha() { + $url = 'https://www.google.com/recaptcha/api/siteverify'; + $data = [ + 'secret' => OPTION_RECAPTCHA_SECRET, + 'response' => $_POST['g-recaptcha-response'], + ]; + $headers = [ + "Content-Type: application/x-www-form-urlencoded", + ]; + + $options = array( + 'http' => array( + 'header' => $headers, + 'method' => 'POST', + 'content' => http_build_query($data), + 'ignore_errors' => TRUE, + ) + ); + $context = stream_context_create($options); + $result = file_get_contents($url, false, $context); + $result = json_decode($result); + if ($result->success) { + return; + } + return join(', ', $result->{'error-codes'}); +} + +function check_for_stripe_submission() { + // If a get request with a stripe parameter + // Run the script session and return either + // the success json or an error + if (isset($_GET['stripe']) && $_GET['stripe']) { + $error = verify_recaptcha(); + if ($error) { + $result = array('error' => $error); + } else { + $result = stripe_session_ajax(); + } + header('Content-Type: application/json'); + print json_encode($result); + exit; + } +} + +function get_checked($value, $checked_value, $echo = true) { + $checked = ($value == $checked_value) ? 'checked="checked"' : ''; + + if ($echo) { + echo $checked; + } else { + return $checked; + } +} + +function wp_esc_attr($text) { + return htmlspecialchars($text, ENT_QUOTES, 'UTF-8'); +} + +function get_or_empty($name){ + return isset($_GET[$name]) ? $_GET[$name] : ''; +} \ No newline at end of file diff --git a/www/includes/easyparliament/templates/html/donate/_stripe_donate.php b/www/includes/easyparliament/templates/html/donate/_stripe_donate.php index 4f65e1b3fd..edea4844b0 100644 --- a/www/includes/easyparliament/templates/html/donate/_stripe_donate.php +++ b/www/includes/easyparliament/templates/html/donate/_stripe_donate.php @@ -1,12 +1,144 @@ + array( + '5' => '£5', + '10' => '£10', + '25' => '£25', + ), + 'one-off' => array( + '25' => '£25', + '50' => '£50', + '100' => '£100', + ), +); +$default_amounts = array( + 'recurring' => '10', + 'one-off' => '50', +); + +# use the how-often parameter if set, if not default to option at end of line (options are 'recurring' or 'one-off') +$initial_payment_type = isset($_GET['how-often']) ? $_GET['how-often'] : 'one-off'; + +# use the how-much parameter if set, if not default to default amount for initial payment type +$how_much = isset($_GET['how-much']) ? $_GET['how-much'] : $default_amounts[$initial_payment_type]; + +# if how-much is not in the allowed values for the current payment type, set to 'other', and set $other_how_much to the value of how-much +if (!array_key_exists($how_much, $payment_amounts[$initial_payment_type])) { + $how_much = 'other'; + $other_how_much = $_GET['how-much']; +} else { + $other_how_much = ''; +} + +?> + + \ No newline at end of file + +