171111 アクセシビリティ検証ツールとしてのNVDA入門 で使ったデモサイト(サーバサイドのコードは一部省略)
{% extends "layout.html" %} | |
{% block content %} | |
<div class="card"> | |
<img class="card-img-top" src="https://placehold.jp/24/cc9999/993333/318x180.png" alt="商品「スマイル」の写真"> | |
<div class="card-body"> | |
<h2 class="card-text">スマイル 1000円</h2> | |
<p class="card-text">このサイトは pay.jp のテストモードで動作しています。</p> | |
</div> | |
</div> | |
{% if email is none %} | |
<div class="row"> | |
<div class="col"> | |
<p>購入するにはGoogleアカウントでログインしてください</p> | |
</div> | |
</div> | |
{% else %} | |
<div class="row"> | |
<div class="col"> | |
<h2>クレジットカードで購入</h2> | |
<div id="pj_guide" role="status" aria-live="assertive" class="alert alert-primary">カード情報を入力して「確認」を押してください</div> | |
</div> | |
</div> | |
<div class="form-row"> | |
<div class="col-12"> | |
<label for="card_number">カード番号</label> | |
<input type="text" id="card_number" class="form-control" aria-describedby="card_number_help" value="5555555555554444" required aria-required="true" pattern="[0-9]{13,16}" /> | |
<small id="card_number_help" class="form-text text-muted">半角数字 <span aria-hidden="true">省略できません</span></small> | |
</div> | |
<div class="col-6"> | |
<label for="card_exp_year">有効期限(年)</label> | |
<input type="text" id="card_exp_year" class="form-control" aria-describedby="card_exp_year_help" value="2020" required aria-required="true" pattern="[0-9]{2,4}" /> | |
<small id="card_exp_year_help" class="form-text text-muted">半角数字 <span aria-hidden="true">省略できません</span></small> | |
</div> | |
<div class="col-6"> | |
<label for="card_exp_month">有効期限(月)</label> | |
<input type="text" id="card_exp_month" class="form-control" aria-describedby="card_exp_month_help" value="12" required aria-required="true" pattern="[0-9]{1,2}" /> | |
<small id="card_exp_month_help" class="form-text text-muted">半角数字 <span aria-hidden="true">省略できません</span></small> | |
</div> | |
<div class="col-12"> | |
<label for="card_cvc">カード確認コード(CVC)</label> | |
<input type="text" id="card_cvc" class="form-control" aria-describedby="card_cvc_help" value="111" pattern="[0-9]{3,4}" /> | |
<small id="card_cvc_help" class="form-text text-muted">半角数字</small> | |
</div> | |
<div class="col"> | |
<button id="get_token_btn" onclick="getToken()" class="form-control btn btn-primary">確認</button> | |
</div> | |
</div> | |
<div class="row"> | |
<div class="col"> | |
<div id="pj_alert" role="status" aria-live="assertive" class="alert alert-light"></div> | |
</div> | |
</div> | |
<form id="pay_form" action="/pay" method="post" class="form-row"> | |
<input type="hidden" name="pj_token" id="pj_token" value="" /> | |
<div class="col"> | |
<button type="submit" id="pj_submit_btn" disabled="disabled" class="form-control btn btn-primary">購入する</button> | |
</div> | |
</form> | |
<div class="form-row"> | |
<div class="col"> | |
<button id="reset_token_btn" onclick="resetToken()" disabled="disabled" class="form-control btn btn-secondary">キャンセル</button> | |
</div> | |
</div> | |
<script type="text/javascript" src="https://js.pay.jp/"></script> | |
<script type="text/javascript" src="/static/index.js"></script> | |
<script type="text/javascript"> | |
Payjp.setPublicKey("{{ public_key }}"); | |
</script> | |
{% endif %} | |
{% endblock %} |
"use strict"; | |
var clearAlert = function () { | |
$('#pj_alert').text('').addClass('alert-light') | |
.removeClass('alert-success').removeClass('alert-warning'); | |
}; | |
var setAlertSuccess = function (message) { | |
$('#pj_guide').text('').addClass('alert-light'); | |
$('#pj_alert').text(message).addClass('alert-success') | |
.removeClass('alert-light').removeClass('alert-warning'); | |
}; | |
var setAlertWarning = function (message) { | |
$('#pj_alert').text(message).addClass('alert-warning') | |
.removeClass('alert-success').removeClass('alert-light'); | |
}; | |
var setGuidePrimary = function (message) { | |
$('#pj_alert').text('').addClass('alert-light') | |
.removeClass('alert-success').removeClass('alert-warning'); | |
$('#pj_guide').text(message).addClass('alert-primary') | |
.removeClass('alert-light'); | |
}; | |
var getToken = function () { | |
var card = { | |
number: $('#card_number').val(), | |
exp_year: $('#card_exp_year').val(), | |
exp_month: $('#card_exp_month').val(), | |
cvc: $('#card_cvc').val() | |
}; | |
clearAlert(); | |
$('#card_number').attr('aria-invalid', false); | |
$('#card_exp_year').attr('aria-invalid', false); | |
$('#card_exp_month').attr('aria-invalid', false); | |
$('#card_cvc').attr('aria-invalid', false); | |
Payjp.createToken(card, function(status, response) { | |
if (status === 200) { | |
$('#pj_token').val(response.id); | |
$('#get_token_btn').prop('disabled', true); | |
$('#reset_token_btn').prop('disabled', false); | |
$('#pj_submit_btn').prop('disabled', false).focus(); | |
$('#card_number').attr('readonly', true); | |
$('#card_exp_year').attr('readonly', true); | |
$('#card_exp_month').attr('readonly', true); | |
$('#card_cvc').attr('readonly', true); | |
setAlertSuccess('カード情報が確認できました。「購入する」を押すと1000円のお支払いが完了します。'); | |
} else { | |
//console.log(response); | |
if (typeof response.error != 'undefined') { | |
if (response.error.code === 'invalid_number') { | |
$('#card_number').attr('aria-invalid', true); | |
setAlertSuccess('カード番号が正しくありません'); | |
} else if (response.error.code === 'invalid_expiry_year') { | |
$('#card_exp_year').attr('aria-invalid', true); | |
setAlertSuccess('有効期限(年)が正しくありません'); | |
} else if (response.error.code === 'invalid_expiry_month') { | |
$('#card_exp_month').attr('aria-invalid', true); | |
setAlertSuccess('有効期限(月)が正しくありません'); | |
} else if (response.error.code === 'invalid_cvc') { | |
$('#card_cvc').attr('aria-invalid', true); | |
setAlertSuccess('カード確認コード(CVC)が正しくありません'); | |
} else { | |
setAlertWarning('カード情報が確認できません。'); | |
} | |
} | |
}; | |
}); | |
}; | |
var resetToken = function () { | |
$('#pj_token').val(''); | |
$('#get_token_btn').prop('disabled', false).focus(); | |
$('#reset_token_btn').prop('disabled', true); | |
$('#pj_submit_btn').prop('disabled', true); | |
$('#card_number').attr('readonly', false); | |
$('#card_exp_year').attr('readonly', false); | |
$('#card_exp_month').attr('readonly', false); | |
$('#card_cvc').attr('readonly', false); | |
setGuidePrimary('カード情報を入力して「確認」を押してください'); | |
}; |
h1 { | |
margin-top: 3rem; | |
margin-bottom: 1rem; | |
} | |
h2 { | |
margin-top: 0.5rem; | |
margin-bottom: 0.5rem; | |
} | |
.card { | |
width: 18rem; | |
margin-bottom: 0.5rem; | |
} | |
button { | |
margin-top: 1rem; | |
margin-bottom: 1rem; | |
} | |
label { | |
margin-top: 1rem; | |
} | |
footer { | |
margin-top: 3rem; | |
} | |
#pj_guide:empty, #pj_alert:empty { | |
/* display: none; */ | |
height: 1px; | |
margin: 0; | |
padding: 0; | |
} | |
[aria-invalid="true"] { | |
border-width: thick; | |
} |
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> | |
<title>ショッピング</title> | |
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" integrity="sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb" crossorigin="anonymous"> | |
<link rel="stylesheet" href="/static/layout.css"> | |
</head> | |
<body class="container"> | |
<main role="main"> | |
<h1>ショッピング</h1> | |
{% block content %}{% endblock %} | |
</main> | |
<footer class="row" role="contentinfo"> | |
<div class="col-12"> | |
{% if email is none %} | |
<p class="text-left"><a href="{{ login_url }}">ログインする</a></p> | |
{% else %} | |
<p class="text-left">Googleアカウント {{ email }}</p> | |
<p class="text-left"><a href="{{ logout_url }}">ログアウトする</a></p> | |
{% endif %} | |
</div> | |
<div class="col"> | |
<p class="text-left">サイト管理者 <a href="https://ja.nishimotz.com/">nishimotz</a></p> | |
<p class="text-left">このサイトは入力されたパスワードを記録しません</p> | |
<p class="text-left">クレジットカードの情報は pay.jp にのみ送信されます</p> | |
<p class="text-left">事前に入力されているのはテスト用のクレジットカード番号です</p> | |
</div> | |
</footer> | |
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.3/umd/popper.min.js" integrity="sha384-vFJXuSJphROIrBnz7yo7oB41mKfc8JzQZiCq4NCceLEaO4IHwicKwpJf9c9IpFgh" crossorigin="anonymous"></script> | |
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js" integrity="sha384-alpBpkh1PFOepccYVYDB4do5UnbKysX5WZXm3XxPqe5iKTfUKjNkCk9SaVuEZflJ" crossorigin="anonymous"></script> | |
</body> | |
</html> |
app = Flask(__name__) | |
def render_index(): | |
user = users.get_current_user() | |
email = user.email() if user else None | |
login_url = users.create_login_url('/') | |
logout_url = users.create_logout_url('/') | |
return render_template('index.html', public_key=PUBLIC_KEY, email=email, login_url=login_url, logout_url=logout_url) | |
@app.route('/') | |
def index(): | |
return render_index() | |
@app.route('/pay', methods=['POST']) | |
def pay(): | |
user = users.get_current_user() | |
email = user.email() if user else None | |
if not email: | |
return render_index() | |
logout_url = users.create_logout_url('/') | |
amount = 1000 | |
customer = payjp.Customer.create( | |
email=user.email(), | |
card=request.form['pj_token'] | |
) | |
payjp.Charge.create( | |
amount=amount, | |
currency='jpy', | |
customer=customer.id, | |
description='example charge' | |
) | |
return render_template('pay.html', amount=amount, email=email, logout_url=logout_url) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment