Adds payment page

Instead of sending all the payment details by email, we're now sending
only a link to a page on this platform. This is to help reduce questions
of whether the emails are from scammers or real.
This commit is contained in:
Hugo Peixoto 2023-07-12 19:13:08 +01:00
parent 8b4e0c9d62
commit 9058ed98f7
31 changed files with 271 additions and 83 deletions

View File

@ -96,6 +96,10 @@ Para o projecto funcionar a 100%, precisa de algumas dependências externas:
Todas estas coisas são configuráveis via variáveis de ambiente. A lista de
variáveis está disponível no ficheiro `env.example`.
**NOTA**: o projecto ainda não está pronto para ser personalizado à imagem de
outras associações, estando o contexto da ANSOL codificado em vários pontos da
aplicação. Alguns destes detalhes podem ser definidos em `app/lib/config.rb`.
## Pôr isto a dar
@ -113,6 +117,6 @@ Quando se acede à plataforma pela primeira vez, é-nos pedido um endereço de
email para criar a primeira conta de administração. Este endereço precisa de
conseguir receber mensagens para o processo de recuperação de senha funcionar.
Depois deste processo terminado, é possível gerir até 5 contas de
Depois deste processo terminado, é possível gerir até 6 contas de
administração. Atenção que qualquer uma delas consegue apagar as restantes (e
até a si própria).

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
width="87.3125mm"
height="10.583333mm"
id="svg3003"
version="1.1">
<defs
id="defs3" />
<metadata
id="metadata4">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
transform="translate(-112.14286,-106.64789)">
<g
id="g12"
transform="matrix(0.125,0,0,-0.125,112.14286,146.64789)">
<path
d="m 0,0 v 200 c 0,66.273 53.7266,120 120,120 h 240 c 66.273,0 120,-53.727 120,-120 V 0 H 400 V 120 H 80 V 0 Z m 80,200 v 0 c 0,22.09 17.9102,40 40,40 h 240 c 22.09,0 40,-17.91 40,-40 z M 560,0 v 200 c 0,66.273 53.727,120 120,120 h 240 c 66.273,0 120,-53.727 120,-120 V 0 h -80 v 200 c 0,22.09 -17.91,40 -40,40 H 680 c -22.09,0 -40,-17.91 -40,-40 V 0 Z m 580,0 h 360 c 55.23,0 100,44.7734 100,100 0,55.227 -44.77,100 -100,100 h -280 c -11.04,0 -20,8.957 -20,20 0,11.043 8.96,20 20,20 h 360 v 80 h -360 c -55.23,0 -100,-44.773 -100,-100 0,-55.227 44.77,-100 100,-100 h 280 c 11.04,0 20,-8.957 20,-20 0,-11.043 -8.96,-20 -20,-20 h -360 z m 540,120 v 80 c 0,66.273 53.73,120 120,120 h 240 c 66.27,0 120,-53.727 120,-120 V 120 C 2160,53.7266 2106.27,0 2040,0 h -240 c -66.27,0 -120,53.7266 -120,120 z m 80,0 v 80 c 0,22.09 17.91,40 40,40 h 240 c 22.09,0 40,-17.91 40,-40 v -80 c 0,-22.0898 -17.91,-40 -40,-40 h -240 c -22.09,0 -40,17.9102 -40,40 z m 480,200 V 120 C 2240,53.7266 2293.73,0 2360,0 h 280 v 80 h -280 c -22.09,0 -40,17.9102 -40,40 v 200"
style="fill:#FFFFFF;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path14" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -1,10 +1,38 @@
/* Application styles */
body {
max-width: 1000px;
margin: 0 auto;
margin: 0px;
}
main, nav, footer {
margin: 0 auto;
max-width: 1000px;
}
main {
padding: 10px;
}
.banner {
width: 100%;
height: 30px;
padding: 15px;
background-image: url(logo.svg);
background-size: auto 30px;
background-repeat: no-repeat;
background-position: center;
background-color: #041952;
}
footer {
border-top: 1px solid #aaa;
margin-top: 20px;
padding: 20px 10px;
}
footer div + div { margin-top: 10px; }
table {
border: 1px solid black;
border-collapse: collapse;

View File

@ -3,7 +3,7 @@ class BoardsController < ApplicationController
def edit
@users = User.where(active: true)
@users += Array.new(5 - @users.size) { User.new }
@users += Array.new(6 - @users.size) { User.new }
end
def update

View File

@ -0,0 +1,5 @@
class PaymentsController < ApplicationController
def show
@payment = Payment.find(params[:id])
end
end

34
app/lib/config.rb Normal file
View File

@ -0,0 +1,34 @@
module Config
def self.organization_full_name
"ANSOL - Associação Nacional para o Software Livre"
end
def self.organization_website
"https://ansol.org"
end
def self.regular_payment_value
30
end
def self.reduced_payment_value
6
end
def self.ifthenpay_payment_title
"Quotas ANSOL"
end
def self.payment_iban
"PT50 0035 2178 00027478430 14"
end
def self.payment_proof_email
"direccao@ansol.org"
end
def self.reduced_payment_description
"Caso queiras usufruir da quota reduzida de 6.00€ para estudantes, pessoas
desempregadas ou reformadas, envia-nos um comprovativo desse estatuto."
end
end

View File

@ -33,6 +33,6 @@ class NotificationMailer < ApplicationMailer
private
def set_notification
@notification = params[:notification]
@link = @notification.member.regular_ifthenpay_link
@payment = @notification.member.create_payment
end
end

View File

@ -2,16 +2,25 @@ class Member < ApplicationRecord
default_scope { where(excluded: false) }
has_many :contributions
has_many :notifications
has_many :payments
has_paper_trail
def self.ransackable_attributes(auth_object = nil)
%w[display_name legal_name email identification_number]
end
def create_payment
payments.create(status: "pending")
end
def cancelled_on
expires_on + 90.days
end
def obtains_full_rights_on
joined_on + 6.months
end
def reset_status!
update(status: expected_status)
end
@ -36,7 +45,7 @@ class Member < ApplicationRecord
def expected_status
if joined_on.nil?
:pending
elsif (joined_on + 6.months).future?
elsif obtains_full_rights_on.future?
:passive
elsif expires_on.future?
:active
@ -82,14 +91,14 @@ class Member < ApplicationRecord
def generate_missing_ifthenpay_links!
self.regular_ifthenpay_link = IfThenPay.generate_gateway_link(
id: number,
amount: "30.00",
description: "Quotas ANSOL",
amount: "%.2f" % Config.regular_payment_value,
description: Config.ifthenpay_payment_title,
) unless self.regular_ifthenpay_link.present?
self.reduced_ifthenpay_link = IfThenPay.generate_gateway_link(
id: number,
amount: "6.00",
description: "Quotas ANSOL",
amount: "%.2f" % Config.reduced_payment_value,
description: Config.ifthenpay_payment_title,
) unless self.reduced_ifthenpay_link.present?
save!

3
app/models/payment.rb Normal file
View File

@ -0,0 +1,3 @@
class Payment < ApplicationRecord
belongs_to :member
end

View File

@ -11,6 +11,7 @@
</head>
<body>
<div class='banner'></div>
<% if signed_in? %>
<nav>
<ul>
@ -28,6 +29,19 @@
</nav>
<% end %>
<main>
<%= yield %>
</main>
<footer>
<div>
<%= link_to Config.organization_full_name, Config.organization_website %>
</div>
<div>
<%= t('footer.description') %>
<%= t('footer.free_software_html') %>
</div>
</footer>
</body>
</html>

View File

@ -0,0 +1,4 @@
<p>
Saudações livres,<br>
Direcção da ANSOL
</p>

View File

@ -0,0 +1,2 @@
Saudações livres,
Direcção da ANSOL

View File

@ -3,20 +3,8 @@
MBWAY:
</p>
<ul>
<li>Valor: 30.00€</li>
<li>Transferência bancária: <strong>PT50 0035 2178 00027478430 14</strong></li>
<li>Multibanco ou MBWAY: <a href="<%= ifthenpay %>"><%= ifthenpay %></a></li>
</ul>
<p>
Caso queiras usufruir da quota reduzida de 6.00€ para estudantes, pessoas
desempregadas ou reformadas, pedimos que nos envies um comprovativo desse
estatuto e faças o pagamento por transferência bancária.
</p>
<p>
Se optares pelo método de transferência bancária, pedimos que envies o
comprovativo de transferência em resposta a este email ou para o endereço
direccao@ansol.org.
</p>
<div align="center">
<a style="border: 1px solid #ccc; background-color: #eee; margin: 30px; display: inline-block; padding: 10px 20px; margin: 0px auto;" href="<%= payment_url(payment) %>">
<%= t('notification_mailer.payment_cta') %>
</a>
</div>

View File

@ -1,14 +1,3 @@
Aceitamos pagamento via transferência bancária, referência multibanco ou
MBWAY:
Aceitamos pagamento via transferência bancária, referência multibanco ou MBWAY:
* Valor: 30.00€
* Transferência bancária: PT50 0035 2178 00027478430 14
* Multibanco ou MBWAY: <%= @link %>
Caso queiras usufruir da quota reduzida de 6.00€ para estudantes, pessoas
desempregadas e reformadas, pedimos que nos envies um comprovativo desse
estatuto e faças o pagamento por transferência bancária.
Se optares pelo método de transferência bancária, pedimos que envies o
comprovativo de transferência em resposta a este email ou para o endereço
direccao@ansol.org.
<%= payment_url(payment) %>

View File

@ -16,9 +16,6 @@
das quotas até <strong><%= @notification.member.expires_on %></strong>.
</p>
<%= render partial: "payment", locals: { ifthenpay: @link } %>
<%= render partial: "payment", locals: { payment: @payment } %>
<p>
Saudações livres,<br>
Direcção da ANSOL
</p>
<%= render partial: "cheers" %>

View File

@ -10,7 +10,6 @@ participação.
Para estender a tua inscrição por mais um ano, pedimos que faças o pagamento
das quotas até <%= @notification.member.expires_on %>.
<%= render partial: "payment", locals: { ifthenpay: @link } %>
<%= render partial: "payment", locals: { payment: @payment } %>
Saudações livres,
Direcção da ANSOL
<%= render partial: "cheers" %>

View File

@ -16,9 +16,6 @@
das quotas até <strong><%= @notification.member.expires_on %></strong>.
</p>
<%= render partial: "payment", locals: { ifthenpay: @link } %>
<%= render partial: "payment", locals: { payment: @payment } %>
<p>
Saudações livres,<br>
Direcção da ANSOL
</p>
<%= render partial: "cheers" %>

View File

@ -10,7 +10,6 @@ participação.
Para estender a tua inscrição por mais um ano, pedimos que faças o pagamento
das quotas até <%= @notification.member.expires_on %>.
<%= render partial: "payment", locals: { ifthenpay: @link } %>
<%= render partial: "payment", locals: { payment: @payment } %>
Saudações livres,
Direcção da ANSOL
<%= render partial: "cheers" %>

View File

@ -12,7 +12,7 @@
nossas actividades. Gostaríamos de continuar a contar com a tua participação.
</p>
<%= render partial: "payment", locals: { ifthenpay: @link } %>
<%= render partial: "payment", locals: { payment: @payment } %>
<p>
Caso não recebamos o pagamento até dia <%= @notification.member.cancelled_on
@ -20,7 +20,4 @@
esclarecer qualquer dúvida através do endereço direccao@ansol.org.
</p>
<p>
Saudações livres,<br>
Direcção da ANSOL
</p>
<%= render partial: "cheers" %>

View File

@ -6,11 +6,10 @@ contribuição anual.
Dependemos exclusivamente da contribuição dos nossos membros para suportar as
nossas actividades. Gostaríamos de continuar a contar com a tua participação.
<%= render partial: "payment", locals: { ifthenpay: @link } %>
<%= render partial: "payment", locals: { payment: @payment } %>
Caso não recebamos o pagamento até dia <%= @notification.member.cancelled_on
%>, cancelaremos permanentemente a tua inscrição. Estamos disponíveis para
esclarecer qualquer dúvida através do endereço direccao@ansol.org.
Saudações livres,
Direcção da ANSOL
<%= render partial: "cheers" %>

View File

@ -12,7 +12,7 @@
nossas actividades. Gostaríamos de continuar a contar com a tua participação.
</p>
<%= render partial: "payment", locals: { ifthenpay: @link } %>
<%= render partial: "payment", locals: { payment: @payment } %>
<p>
Caso não recebamos o pagamento até dia <%= @notification.member.cancelled_on
@ -20,7 +20,4 @@
esclarecer qualquer dúvida através do endereço direccao@ansol.org.
</p>
<p>
Saudações livres,
Direcção da ANSOL
</p>
<%= render partial: "cheers" %>

View File

@ -6,11 +6,10 @@ a tua contribuição anual.
Dependemos exclusivamente da contribuição dos nossos membros para suportar as
nossas actividades. Gostaríamos de continuar a contar com a tua participação.
<%= render partial: "payment", locals: { ifthenpay: @link } %>
<%= render partial: "payment", locals: { payment: @payment } %>
Caso não recebamos o pagamento até dia <%= @notification.member.cancelled_on
%>, cancelaremos permanentemente a tua inscrição. Estamos disponíveis para
esclarecer qualquer dúvida através do endereço direccao@ansol.org.
Saudações livres,
Direcção da ANSOL
<%= render partial: "cheers" %>

View File

@ -12,7 +12,7 @@
nossas actividades. Gostaríamos de continuar a contar com a tua participação.
</p>
<%= render partial: "payment", locals: { ifthenpay: @link } %>
<%= render partial: "payment", locals: { payment: @payment } %>
<p>
Caso não recebamos o pagamento até dia <%= @notification.member.cancelled_on
@ -20,7 +20,4 @@
esclarecer qualquer dúvida através do endereço direccao@ansol.org.
</p>
<p>
Saudações livres,<br>
Direcção da ANSOL
</p>
<%= render partial: "cheers" %>

View File

@ -6,11 +6,10 @@ a tua contribuição anual.
Dependemos exclusivamente da contribuição dos nossos membros para suportar as
nossas actividades. Gostaríamos de continuar a contar com a tua participação.
<%= render partial: "payment", locals: { ifthenpay: @link } %>
<%= render partial: "payment", locals: { payment: @payment } %>
Caso não recebamos o pagamento até dia <%= @notification.member.cancelled_on
%>, cancelaremos permanentemente a tua inscrição. Estamos disponíveis para
esclarecer qualquer dúvida através do endereço direccao@ansol.org.
Saudações livres,
Direcção da ANSOL
<%= render partial: "cheers" %>

View File

@ -0,0 +1,60 @@
<h1>Pagamento de quotas</h1>
<p>
Olá <strong><%= @payment.member.display_name %></strong>.
Para estender a tua inscrição por
mais um ano, pedimos que faças o pagamento das quotas até
<strong><%= @payment.member.expires_on %></strong>.
</p>
<p><%= Config.reduced_payment_description %></p>
<p>Aceitamos pagamento via transferência bancária, referência multibanco ou MBWAY.</p>
<div>
<input class="show-hide-toggle" type='checkbox' id='regular' />
<label class="show-hide-button" for="regular">Pagamento normal (<%= number_with_precision(Config.regular_payment_value, precision: 2) %>€)</label>
<div class="show-hide-box">
<p>Valor: <%= number_with_precision(Config.regular_payment_value, precision: 2) %>€</p>
<p><%= link_to "Pagar por Multibanco ou MBWAY", @payment.member.regular_ifthenpay_link, class: 'button' %></p>
<p>Pagar por transferência bancária:</p>
<ul>
<li>IBAN: <strong><%= Config.payment_iban %></strong></li>
<li>obrigatório enviar comprovativo de pagamento para <%= Config.payment_proof_email %></li>
</ul>
</div>
</div>
<div style='margin-top: 10px;'>
<input class="show-hide-toggle" type='checkbox' id='reduced' />
<label class="show-hide-button" for="reduced">Pagamento reduzido (<%= number_with_precision(Config.reduced_payment_value, precision: 2) %>€)</label>
<div class="show-hide-box">
<p>Valor: <%= number_with_precision(Config.reduced_payment_value, precision: 2) %>€</p>
<p><%= link_to "Pagar por Multibanco ou MBWAY", @payment.member.reduced_ifthenpay_link, class: 'button' %></p>
<p>Pagar por transferência bancária:</p>
<ul>
<li>IBAN: <strong><%= Config.payment_iban %></strong></li>
<li>obrigatório enviar comprovativo de pagamento para <%= Config.payment_proof_email %></li>
</ul>
</div>
</div>
<style>
.button { border: 1px solid #ccc; background-color: #eee; cursor: pointer; padding: 4px 10px; display: inline-block; text-align: center; }
.show-hide-button { border: 1px solid #ccc; background-color: #eee; cursor: pointer; padding: 4px; display: block; text-align: center; }
.show-hide-toggle { display: none; }
.show-hide-toggle:not(:checked) ~ .show-hide-box { display: none; }
.show-hide-toggle:checked ~ .show-hide-box {
display: block;
overflow: auto;
padding: 10px 30px;
border: 1px solid #ccc;
border-top: 0;
}
.show-hide-box ul {
margin-left: 30px;
}
</style>

View File

@ -5,6 +5,9 @@ en:
notifications: "Notificações"
contributions: "Contributions"
due_contributions: "Dues"
footer:
description: saucy Membership management platform.
free_software_html: It's <a href="https://git.ansol.org/ansol/saucy">Free Software</a>.
members:
index:
title: "Members"
@ -61,6 +64,7 @@ en:
year after that. You can override the expiration date by setting a date
below.
notification_mailer:
payment_cta: "Pay now"
expiration_in_60d:
subject: "ANSOL - Pagamento anual de quotas"
title: "Pagamento anual de quotas"

View File

@ -8,6 +8,9 @@ pt:
notifications: "Notificações"
contributions: "Contribuições"
due_contributions: "Dívidas"
footer:
description: saucy Plataforma de gestão de sócios.
free_software_html: É <a href="https://git.ansol.org/ansol/saucy">Software Livre</a>.
members:
delete:
confirmation_message: |
@ -99,6 +102,7 @@ pt:
due:
title: "Contribuições em dívida"
notification_mailer:
payment_cta: "Pagar quotas"
expiration_in_60d:
subject: "ANSOL - Pagamento anual de quotas"
expiration_in_30d:

View File

@ -32,5 +32,7 @@ Rails.application.routes.draw do
resource :board, only: [:edit, :update]
resource :letters, only: [:create]
resources :payments, only: [:show]
resource :initial_setup, only: [:create, :show]
end

View File

@ -0,0 +1,11 @@
class CreatePayments < ActiveRecord::Migration[7.0]
def change
create_table :payments, id: :uuid do |t|
t.references :member, type: :uuid, foreign_key: true, null: false
t.string :status
t.timestamps
end
end
end

11
db/schema.rb generated
View File

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.0].define(version: 2023_03_31_005733) do
ActiveRecord::Schema[7.0].define(version: 2023_07_12_103042) do
# These are extensions that must be enabled in order to support this database
enable_extension "pgcrypto"
enable_extension "plpgsql"
@ -60,6 +60,14 @@ ActiveRecord::Schema[7.0].define(version: 2023_03_31_005733) do
t.index ["member_id"], name: "index_notifications_on_member_id"
end
create_table "payments", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.uuid "member_id", null: false
t.string "status"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["member_id"], name: "index_payments_on_member_id"
end
create_table "users", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
@ -85,4 +93,5 @@ ActiveRecord::Schema[7.0].define(version: 2023_03_31_005733) do
add_foreign_key "contributions", "members"
add_foreign_key "notifications", "members"
add_foreign_key "payments", "members"
end

View File

@ -84,7 +84,7 @@ namespace :saucy do
category = case member['membership']['membership_type_id']
when "1" then 'student'
when "2" then 'normal'
when "2" then 'employed'
when "3" then 'retired'
when "4" then 'unemployed'
else raise "derp membership type"