Skip to content

Commit

Permalink
collect postal addresses for some payments
Browse files Browse the repository at this point in the history
  • Loading branch information
Changaco committed Dec 5, 2021
1 parent 7b6eaed commit a53dfc0
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 31 deletions.
23 changes: 17 additions & 6 deletions js/stripe.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,21 @@ Liberapay.stripe_form_init = function($form) {
$form.submit();
return;
}
var local_address = $form.find('input[name="postal_address.local_address"]').val();
local_address = !!local_address ? local_address.split('\n') : [null];
if (local_address.length === 1) {
local_address.push(null);
}
if (element_type == 'iban') {
var tokenData = {};
tokenData.currency = 'EUR';
tokenData.account_holder_name = $form.find('input[name="owner.name"]').val();
tokenData.address_country = $form.find('input[name="postal_address.country"]').val();
tokenData.address_state = $form.find('input[name="postal_address.region"]').val();
tokenData.address_city = $form.find('input[name="postal_address.city"]').val();
tokenData.address_zip = $form.find('input[name="postal_address.postal_code"]').val();
tokenData.address_line1 = local_address[0];
tokenData.address_line2 = local_address[1];
stripe.createToken(element, tokenData).then(Liberapay.wrap(function(result) {
if (result.error) {
$errorElement.text(result.error.message);
Expand All @@ -85,12 +96,12 @@ Liberapay.stripe_form_init = function($form) {
var pmData = {
billing_details: {
address: {
city: $form.find('input[name="owner.address.city"]').val(),
country: $form.find('input[name="owner.address.country"]').val(),
line1: $form.find('input[name="owner.address.line1"]').val(),
line2: $form.find('input[name="owner.address.line2"]').val(),
postal_code: $form.find('input[name="owner.address.postal_code"]').val(),
state: $form.find('input[name="owner.address.state"]').val(),
city: $form.find('input[name="postal_address.city"]').val(),
country: $form.find('input[name="postal_address.country"]').val(),
line1: local_address[0],
line2: local_address[1],
postal_code: $form.find('input[name="postal_address.postal_code"]').val(),
state: $form.find('input[name="postal_address.region"]').val(),
},
email: $form.find('input[name="owner.email"]').val(),
name: $form.find('input[name="owner.name"]').val(),
Expand Down
3 changes: 0 additions & 3 deletions liberapay/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -378,9 +378,6 @@ def __missing__(self, currency):
POSTAL_ADDRESS_KEYS_LIBERAPAY = (
'country', 'region', 'city', 'postal_code', 'local_address'
)
POSTAL_ADDRESS_KEYS_STRIPE = (
'line1', 'line2', 'city', 'state', 'postal_code', 'country'
)

PRIVACY_FIELDS = OrderedDict([
('hide_giving', (_("Hide total giving from others."), False)),
Expand Down
27 changes: 27 additions & 0 deletions liberapay/models/exchange_route.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,33 @@ def get_partial_number(self):
else:
raise NotImplementedError(self.network)

def get_postal_address(self):
if self.network.startswith('stripe-'):
if self.address.startswith('pm_'):
return self.stripe_payment_method.billing_details.address
else:
return self.stripe_source.owner.address
else:
raise NotImplementedError(self.network)

def set_postal_address(self, addr):
if self.network.startswith('stripe-'):
addr = addr.copy()
addr['state'] = addr.pop('region')
addr['line1'], addr['line2'], *_ignore = addr.pop('local_address').split('\n')
if self.address.startswith('pm_'):
self.stripe_payment_method = stripe.PaymentMethod.modify(
self.address,
billing_details={'address': addr},
)
else:
self.stripe_source = stripe.Source.modify(
self.address,
owner={'address': addr},
)
else:
raise NotImplementedError(self.network)

def has_been_charged_successfully(self):
return bool(self.db.one("""
SELECT 1
Expand Down
4 changes: 2 additions & 2 deletions templates/macros/postal-addresses.html
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
% from "templates/macros/icons.html" import glyphicon

% macro postal_address_form_v2(prefix='postal_address', saved=None, country=None, required=True)
% macro postal_address_form_v2(prefix='postal_address', saved=None, required=True)

% set required = 'required' if required else ''

<label>
<span>{{ _("Country") }}</span>
<select name="{{ prefix }}.country" class="form-control country" {{ required }}>
<option></option>
% set country = country or saved.country
% set country = saved.country or request.country
% for code, name in locale.countries.items()
<option value="{{ code }}" {{ 'selected' if code == country }}>{{ name }}</option>
% endfor
Expand Down
54 changes: 47 additions & 7 deletions www/%username/giving/pay/stripe/%payin_id.spt
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@ from liberapay.payin.prospect import PayinProspect
from liberapay.payin.stripe import (
charge, create_source_from_token, repr_stripe_error, settle_payin,
)
from liberapay.utils import get_participant
from liberapay.utils import check_address_v2, get_participant

STRIPE_BIT = 1

[---]

payer = get_participant(state, restrict=True)
identity = payer.get_current_identity() or {}

del currency

Expand Down Expand Up @@ -64,17 +65,22 @@ if request.method == 'POST':
payin_amount, amount_min, amount_max
))

postal_address = {
k: body.get('postal_address.' + k) for k in constants.POSTAL_ADDRESS_KEYS_LIBERAPAY
}
if postal_address.get('local_address'):
if check_address_v2(postal_address):
if postal_address != identity.get('postal_address'):
identity['postal_address'] = postal_address
payer.insert_identity(identity)
else:
postal_address = None

try:
if 'token' in body:
return_url = payer.url('giving/pay/stripe/complete')
one_off = body.get('keep') != 'true'
owner_address = {
k: body.get('owner.address.' + k) for k in constants.POSTAL_ADDRESS_KEYS_STRIPE
}
if 'line1' not in owner_address:
owner_address = None
owner_info = {
'address': owner_address,
'email': payer.get_email_address(),
'name': body.get('owner.name'),
}
Expand Down Expand Up @@ -102,6 +108,10 @@ if request.method == 'POST':
raise response.error(400, _(
"The payment instrument is invalid, please select or add another one."
))

if postal_address and not route.get_postal_address():
route.set_postal_address(postal_address)

transfer_amounts = resolve_amounts(
payin_amount, {tip.id: tip.amount for tip in tips}
)
Expand Down Expand Up @@ -195,6 +205,26 @@ if tippees:
if len(set(tip.amount.currency for tip in tips)) != 1:
raise response.invalid_input(tippees, 'beneficiary', 'querystring')
payment = PayinProspect(payer, tips, 'stripe')
postal_address_is_required = (
payment_type == 'sdd' or
# Note: this isn't really accurate, but it's good enough for now.
website.db.one("""
SELECT true
FROM payment_accounts a
WHERE ( a.participant IN %(tippee_ids)s OR
a.participant IN (
SELECT take.member
FROM current_takes take
WHERE take.team IN %(tippee_ids)s
AND take.amount <> 0
)
)
AND a.provider = 'stripe'
AND a.country = 'IN'
AND a.is_current
LIMIT 1
""", tippee_ids=tuple(tip.tippee for tip in tips))
)
del tips

elif not payin_id:
Expand All @@ -213,6 +243,8 @@ response.headers[b'Content-Security-Policy'] = csp
title = _("Funding your donations")

[---] text/html
% from "templates/macros/postal-addresses.html" import postal_address_form_v2 with context

% extends "templates/layouts/base-thin.html"

% block thin_content
Expand Down Expand Up @@ -469,6 +501,14 @@ title = _("Funding your donations")
</fieldset>
% endif

% if postal_address_is_required
<p>{{ _("Postal address:") }}</p>
<div class="block-labels max-width-500">{{
postal_address_form_v2(saved=identity.get('postal_address'))
}}</div>
<br>
% endif

<div class="text-info">{{ glyphicon('info-sign') }} {{ _(
"Liberapay does not store money, the entire amount of your payment will "
"go immediately to the {payment_provider} accounts of {recipient_names}.",
Expand Down
4 changes: 0 additions & 4 deletions www/%username/identity.spt
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,6 @@ if request.method == 'POST':
if v['city'] or v['postal_code'] or v['local_address']:
if not check_address_v2(v):
error = _("The provided postal address is incomplete.")
else:
newlines = v['local_address'].count('\n')
if newlines > 1:
error = "`local_address` value contains too many newlines (%i > %i)" % (newlines, 1)
for subkey, subvalue in v.items():
if len(subvalue) > 200:
error = "`%s` value is too long (%i > %i)" % (subkey, len(subvalue), 200)
Expand Down
26 changes: 17 additions & 9 deletions www/%username/routes/add.spt
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,20 @@ import stripe

from liberapay.models.exchange_route import ExchangeRoute
from liberapay.payin.stripe import create_source_from_token, repr_stripe_error
from liberapay.utils import form_post_success, get_participant
from liberapay.utils import check_address_v2, form_post_success, get_participant

[---]

participant = get_participant(state, restrict=True)
identity = participant.get_current_identity() or {}

if request.method == 'POST':
body = request.body
one_off = body.get('one_off') == 'true'
return_url = participant.url('routes/')
try:
if 'token' in body:
owner_address = {
k: body.get('owner.address.' + k) for k in constants.POSTAL_ADDRESS_KEYS_STRIPE
}
if 'line1' not in owner_address:
owner_address = None
owner_info = {
'address': owner_address,
'email': participant.get_email_address(),
'name': body.get('owner.name'),
}
Expand Down Expand Up @@ -49,6 +44,13 @@ if request.method == 'POST':
"The payment processor {name} returned an error: “{error_message}”.",
name='Stripe', error_message=repr_stripe_error(e)
))
postal_address = {
k: body.get('postal_address.' + k) for k in constants.POSTAL_ADDRESS_KEYS_LIBERAPAY
}
if check_address_v2(postal_address):
if postal_address != identity.get('postal_address'):
identity['postal_address'] = postal_address
participant.insert_identity(identity)
msg = _("The payment instrument has been successfully added.")
form_post_success(state, redirect_url=participant.path('routes/'), msg=msg)

Expand All @@ -64,9 +66,10 @@ response.headers[b'Content-Security-Policy'] = csp
title = _("Add a payment instrument")

[---] text/html
% extends "templates/layouts/settings.html"

% from "templates/macros/icons.html" import fontawesome
% from "templates/macros/postal-addresses.html" import postal_address_form_v2 with context

% extends "templates/layouts/settings.html"

% block content

Expand Down Expand Up @@ -122,6 +125,11 @@ title = _("Add a payment instrument")
</label>
</fieldset>
% endif
<p>{{ _("Postal address:") }}</p>
<div class="block-labels max-width-500">{{
postal_address_form_v2(saved=identity.get('postal_address'))
}}</div>
<br>
<button class="btn btn-primary btn-lg">{{ _("Save") }}</button>
</form>

Expand Down

0 comments on commit a53dfc0

Please sign in to comment.