Djangoroidの奮闘記

python,django,angularJS1~三十路過ぎたプログラマーの奮闘記

Django e-commerce part74 Run Braintree Transaction

Bratintreeのtransactionを試してみる。

Braintreeのtokenを処理するClient を自分たちのサイトに設定する。

公式ドキュメント参照:

Set Up Your Client | JS - Braintree Developer Documentation

templates/javascript.html に追記する。

ちょっとjsv3は難しそうだったので、v2でやってみる。

<script src="https://js.braintreegateway.com/js/braintree-2.29.0.min.js"></script>

checkout_view.html にjavascriptをコピペしてみる。

<script>
{% block jquery %}
var clientToken = "eyJ2ZXJzaW9uIjoyLCJhdXRob3JpemF0aW9uRmluZ2VycHJpbnQiOiJjMTA5MzkyMWZjMGVkOTZkYWRjMTgwY2I5NzAwMmI5ODkwMGE2ZWE2ZmMxNzEwMzU5MGYwOGQ5NjVjZjFiNzE5fGNyZWF0ZWRfYXQ9MjAxNi0xMC0xMlQwMDoyMjo1NS43NDM2MDE4MDMrMDAwMFx1MDAyNm1lcmNoYW50X2lkPTM0OHBrOWNnZjNiZ3l3MmJcdTAwMjZwdWJsaWNfa2V5PTJuMjQ3ZHY4OWJxOXZtcHIiLCJjb25maWdVcmwiOiJodHRwczovL2FwaS5zYW5kYm94LmJyYWludHJlZWdhdGV3YXkuY29tOjQ0My9tZXJjaGFudHMvMzQ4cGs5Y2dmM2JneXcyYi9jbGllbnRfYXBpL3YxL2NvbmZpZ3VyYXRpb24iLCJjaGFsbGVuZ2VzIjpbXSwiZW52aXJvbm1lbnQiOiJzYW5kYm94IiwiY2xpZW50QXBpVXJsIjoiaHR0cHM6Ly9hcGkuc2FuZGJveC5icmFpbnRyZWVnYXRld2F5LmNvbTo0NDMvbWVyY2hhbnRzLzM0OHBrOWNnZjNiZ3l3MmIvY2xpZW50X2FwaSIsImFzc2V0c1VybCI6Imh0dHBzOi8vYXNzZXRzLmJyYWludHJlZWdhdGV3YXkuY29tIiwiYXV0aFVybCI6Imh0dHBzOi8vYXV0aC52ZW5tby5zYW5kYm94LmJyYWludHJlZWdhdGV3YXkuY29tIiwiYW5hbHl0aWNzIjp7InVybCI6Imh0dHBzOi8vY2xpZW50LWFuYWx5dGljcy5zYW5kYm94LmJyYWludHJlZWdhdGV3YXkuY29tLzM0OHBrOWNnZjNiZ3l3MmIifSwidGhyZWVEU2VjdXJlRW5hYmxlZCI6dHJ1ZSwicGF5cGFsRW5hYmxlZCI6dHJ1ZSwicGF5cGFsIjp7ImRpc3BsYXlOYW1lIjoiQWNtZSBXaWRnZXRzLCBMdGQuIChTYW5kYm94KSIsImNsaWVudElkIjpudWxsLCJwcml2YWN5VXJsIjoiaHR0cDovL2V4YW1wbGUuY29tL3BwIiwidXNlckFncmVlbWVudFVybCI6Imh0dHA6Ly9leGFtcGxlLmNvbS90b3MiLCJiYXNlVXJsIjoiaHR0cHM6Ly9hc3NldHMuYnJhaW50cmVlZ2F0ZXdheS5jb20iLCJhc3NldHNVcmwiOiJodHRwczovL2NoZWNrb3V0LnBheXBhbC5jb20iLCJkaXJlY3RCYXNlVXJsIjpudWxsLCJhbGxvd0h0dHAiOnRydWUsImVudmlyb25tZW50Tm9OZXR3b3JrIjp0cnVlLCJlbnZpcm9ubWVudCI6Im9mZmxpbmUiLCJ1bnZldHRlZE1lcmNoYW50IjpmYWxzZSwiYnJhaW50cmVlQ2xpZW50SWQiOiJtYXN0ZXJjbGllbnQzIiwiYmlsbGluZ0FncmVlbWVudHNFbmFibGVkIjp0cnVlLCJtZXJjaGFudEFjY291bnRJZCI6ImFjbWV3aWRnZXRzbHRkc2FuZGJveCIsImN1cnJlbmN5SXNvQ29kZSI6IlVTRCJ9LCJjb2luYmFzZUVuYWJsZWQiOmZhbHNlLCJtZXJjaGFudElkIjoiMzQ4cGs5Y2dmM2JneXcyYiIsInZlbm1vIjoib2ZmIn0=";

braintree.setup(clientToken, "dropin", {
  container: "payment-form"
});
{% endblock %}
</script>

この、clientToken は、例なので、この部分にclient_tokenを代入する。

<script>
{% block jquery %}
var clientToken = "{{ client_token }}";

braintree.setup(clientToken, "dropin", {
  container: "payment-form"
});
{% endblock %}
</script>

checkout_view.html にbraintreeのフォームを入れてみる。

公式マニュアルよりそのままコピペしてみる。

<form id="checkout" method="post" action="/checkout">
  <div id="payment-form"></div>
  <input type="submit" value="Pay $10">
</form>

これを既存の注文確定ボタンと合体させると以下のようになる。

<form id="checkout" method='POST' action="{% url 'checkout_final' %}">{% csrf_token %}
<!-- <input type='hidden' name='payment_token' value='ABC' /> -->
<div id="payment-form"></div>
<p><b><button type='submit'>注文を確定する</button></b>
</p>
</form>

payment_tokenはコメントアウトしてある。

これで、checkout画面に行くと、braintreeのフォームが表示される。

CheckoutFinalView にpostの内容をprintさせる。

class CheckoutFinalView(CartOrderMixin, View):
    def post(self, request, *args, **kwargs):
        print (request.POST)

これで、postされた内容が確認できる。以下のような辞書がprintされていると思われる。

<QueryDict: {'payment_method_nonce': ['****-0c55-42a9-9588-*****'], 'csrfmiddlewaretoken': ['*******************']}>

この、payment_method_nonce というのが、braintree側に送付され、これがキーとなって、取引の決済がされるはず。

Transaction Sale を追加してみる。

公式ドキュメント:

Transaction.sale() | Python - Braintree Developer Documentation

result = braintree.Transaction.sale({
    "amount": "10.00",
    "payment_method_nonce": nonce_from_the_client,
    "options": {
        "submit_for_settlement": True
    }
})

これをCheckoutFinalViewに組み込んでみる。 この辺りの公式ドキュメントを参照:

Transaction.sale() | Python - Braintree Developer Documentation

class CheckoutFinalView(CartOrderMixin, View):
    def post(self, request, *args, **kwargs):
        order = self.get_order()
        order_total = order.order_total
        nonce = request.POST.get("payment_method_nonce")
        if nonce:
            result = braintree.Transaction.sale({
                "amount": order_total,
                "payment_method_nonce": nonce,
                "billing": {
                    "postal_code": "%s" %(order.billing_address.zipcode),
                  },
                "options": {
                    "submit_for_settlement": True
                }
            })
            if result.is_success:
                print (result.transaction.id)
                # result.transaction.id to order
                order.mark_completed()
                messages.success(request, "ご注文ありがとうございました。")
                del request.session["cart_id"]
                del request.session["order_id"]
            else:
                messages.success(request, "%s" %(result.message))
                return redirect("checkout")

        return redirect("orders_detail", pk=order.pk)

ポイント

  • 全体的な流れとしては、決済した時に生成されるpayment_method_nonceを、keyにして、braintree.Transaction.saleを実行し、resultがサクセスだった場合は、orderをcompletedにして、cart_idなどをセッションから削除する。

braintreeの設定をviewにimportする。

import braintree
from django.conf import settings

if settings.DEBUG:
    braintree.Configuration.configure(braintree.Environment.Sandbox,
        merchant_id=settings.BRAINTREE_MERCHANT,
        public_key=settings.BRAINTREE_PUBLIC,
        private_key=settings.BRAINTREE_PRIVATE)

orders/models.py に、braintreeのtransaction.idを格納する、order_idフィールドを作成する。

ついで、statusもpaidと、shippedに変更しておく。

ORDER_STATUS_CHOICES = (
    ('created', 'Created'),
    ('paid','Paid'),
    ('shipped','Shipped'),
)

class Order(models.Model):
    status = models.CharField(max_length=120, choices=ORDER_STATUS_CHOICES, default='created')
...
    order_id = models.CharField(max_length=20, null=True, blank=True)
...
    def mark_completed(self):
        self.status = "paid"
        self.save()

さらに、mark_completed()に少し追記する。

    def mark_completed(self, order_id=None):
        self.status = "paid"
        if order_id and not self.order_id:
            self.order_id = order_id
        self.save()
  • これは、order_idがすでにある場合は、上書きしないような設定にしている と思われる。

CheckoutFinalViewに追記する。

class CheckoutFinalView(CartOrderMixin, View):
...
            if result.is_success:
                order.mark_completed(order_id=result.transaction.id)
  • result.is_success がtrueの時は、status=paid, order_idには、transaction.idがセットされるように設定してある。