Create a gist now

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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)
{% extends "layout.html" %}
{% block content %}
<div class="row">
<div class="col-12">
<div class="alert alert-success">{{ amount }}円のお支払いが完了しました。</div>
<p><a href="/">戻る</a></p>
</div>
</div>
{% endblock %}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment