静的ファイル、全品目画像は
/usr/share/nginx/html/static
にある
アプリ本体は以下のディレクトリにある
/home/app_admin/venv_ruling/ruling/
静的ファイル、全品目画像は
/usr/share/nginx/html/static
にある
アプリ本体は以下のディレクトリにある
/home/app_admin/venv_ruling/ruling/
app_dir/urls.py
1 2 3 4 5 6 7 |
from django.views.generic import TemplateView urlpatterns = [ <span class="n">path</span><span class="p">(</span> <span class="s">"robots.txt"</span><span class="p">,</span> <span class="n">TemplateView</span><span class="p">.</span><span class="n">as_view</span><span class="p">(</span><span class="n">template_name</span><span class="o">=</span><span class="s">"robots.txt"</span><span class="p">,</span> <span class="n">content_type</span><span class="o">=</span><span class="s">"text/plain"</span><span class="p">),</span> <span class="p">),</span> |
記述内容(google以外のクローラーを拒否)
1 2 3 4 5 |
User-agent: * Disallow: / User-agent: Googlebot Allow: / |
設定の基本はこちらを参照
envファイル読み込み
1 2 3 4 5 6 7 8 9 10 11 |
#env設定 import environ env = environ.Env( # set casting, default value DEBUG=(bool, False) ) env.read_env('.env') # False if not in os.environ DEBUG = env('DEBUG') |
settings.py設定
1 2 3 4 5 6 7 8 9 10 11 12 |
EMAIL_HOST_PASSWORD = env('EMAIL_HOST_PASSWORD') DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': env('DATABASE_NAME'), 'USER': env('USER'), 'PASSWORD': env('DATABASE_PASS'), 'HOST': '', 'PORT': '', } } |
.envファイル設定
1 2 3 4 |
EMAIL_HOST_PASSWORD=SG.xxxxxxxxxxxxxxJ30Ame4ORjxiM880j0eL_3rgU DATABASE_PASS=xxxxxxx DATABASE_NAME=xxxxxxx USER=xxxxxx |
イコール(=)の両側にスペースがあるとエラーになるのでぴったりくっつける。
ローカルからaws等にgitを使用せずにenvファイルを移動する際、googledriveで
別PCに転送すると以下のようンエラーが出る。
django.core.exceptions.ImproperlyConfigured: Set the EMAIL_HOST_PASSWORD environment variable
USBメモリでenvファイルの移動をすれば問題ない。
SendGridの登録方法はLearnDjango様のページ下部Email Serviceを参考
view.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
from django.core.mail import send_mail #メール送信 subject = "Thank you for your subscription to xxx." message = "Dear " + name + "\n\nThank you for your subscription to xxx. \nHere is your Subscriptiondetail " \ "\n\nInvoice.No: " + invoice +\ "\nSubscription ID: " + stripe_customer_id +\ "\nApplication date: " + date +\ "\nName: " + name +\ "\nMonthly Subscription Fee: " + currency.upper() + " " + price_new +\ "\n\nYou will be charged " + currency.upper() + " " + price_new + " every month until you cancel your subscription."\ "\n\nif you have any question please reply this email.\n\nBest Regards \n\nxxx\nxxx." from_email = 'xxx@xxx.com' admin_email = ['xxx@xxx.com',customer_email ] print("send") try: send_mail(subject ,message, from_email, admin_email) except: import traceback traceback.print_exc() |
settings.py
1 2 3 4 5 6 7 |
DEFAULT_FROM_EMAIL = 'xxx@xxxx.com' EMAIL_HOST = 'smtp.sendgrid.net' EMAIL_HOST_USER = 'apikey' EMAIL_HOST_PASSWORD = 'SG.j2IQBU_xxxxxxxxxxxxxxxxxxxxxxxxxx' # EMAIL_HOST_PASSWORD = 'customsxxxxxx' EMAIL_PORT = 587 EMAIL_USE_TLS = True |
Djangoでコンタクトフォームの作成を行う際
Koji Mochizuki様の記事を参考にさせて頂きました。
リンクができないのでURLを以下に貼っておきます。
https://medium.com/@kjmczk/django-contact-form-5a35d43b00a6
しかし、そのままではエラーが出てしまうため修正を行い、
SendGridを活用したSMTP送信の方法を備忘録として記録しておく。
SendGridの登録方法はLearnDjango様のページ下部Email Serviceを参考
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
from django import forms from django.conf import settings from django.core.mail import BadHeaderError, send_mail from django.http import HttpResponse class ContactForm(forms.Form): name = forms.CharField( label='', max_length=100, widget=forms.TextInput(attrs={ 'class': 'form-control', 'placeholder': "お名前", }), ) email = forms.EmailField( label='', widget=forms.EmailInput(attrs={ 'class': 'form-control', 'placeholder': "メールアドレス", }), ) message = forms.CharField( label='', widget=forms.Textarea(attrs={ 'class': 'form-control', 'placeholder': "お問い合わせ内容", }), ) def send_email(self): subject = "お問い合わせ" message = self.cleaned_data['message'] name = self.cleaned_data['name'] email = self.cleaned_data['email'] from_email = 'xxxxxx@xxxxxx.com' admin_email = ['xxxxxx@xxxxxx.com'] #メッセージと名前とメールを結合 send_message = message + "\n" + name + "\n" + email try: send_mail(subject ,send_message, from_email, admin_email) except BadHeaderError: return HttpResponse("無効なヘッダが検出されました。") |
from_emailとadmin_emailに代入するメールアドレスはそれぞれSendGridのSingle Sender VerificationにあるFROMとREPLYと一致させないとエラーになってしまう。
その為、コンタクトフォームから連絡をもらってもfromが自分のアドレスなので
そのまま直接返信ができないという奇妙な状態にあり、これは未解決。
また、admin_emailの部分はリストかタプルでないと受け付けてもらえない、
送信先アドレスをSendGridのSingle Sender Verificationにある
SendGridにSender設定したメールアドレスの確認はSingleSenderVerificationを確認。
Settings > Sender Authentication > Single Sender Verification > Veryfy a Single Senderでも
行ける。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
from django.views.generic import TemplateView from django.views.generic.edit import FormView from .forms import ContactForm class ContactFormView(FormView): template_name = 'contact/contact_form.html' form_class = ContactForm success_url = 'result' def form_valid(self, form): form.send_email() return super().form_valid(form) class ContactResultView(TemplateView): template_name = 'contact/contact_result.html' def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['success'] = "お問い合わせは正常に送信されました。" return context |
1 2 3 4 5 6 7 8 |
from django.urls import path,include from .views import ContactFormView, ContactResultView urlpatterns = [ ... path('contact/', ContactFormView.as_view(), name='contact_form'), path('contact/result/', ContactResultView.as_view(), name='contact_result'), ] |
プロジェクトルートのtemplates内にcontactフォルダを作り、その中にcontact_form.html、contact_result.htmlファイルを新規作成
1 2 3 4 |
templates/ contact/ contact_form.html contact_result.html |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
{% block content %} <div class="container"> <div class="row"> <div class="col-md-8"> <h1>お問い合わせ</h1> <p>お問い合わせフォームです。</p> <form method="POST">{% csrf_token %} {{ form.as_p }} <button type="submit" class="btn btn-primary">送信</button> </form> </div> </div> </div> {% endblock %} |
1 2 3 |
{% block content %} {{ success }} {% endblock %} |
1 2 3 4 5 6 7 8 9 10 |
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' ACCOUNT_EMAIL_VERIFICATION = "none" # sendgrid でメール送信する場合 DEFAULT_FROM_EMAIL = 'xxxxx@xxxxxx.com' EMAIL_HOST = 'smtp.sendgrid.net' EMAIL_HOST_USER = 'apikey' #ここは本当にapikeyと入力する個別の名称ではない EMAIL_HOST_PASSWORD = 'SG.j2Ixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' EMAIL_PORT = 587 EMAIL_USE_TLS = Trueこ |
これでcontact/ページにいけばフォームが設置されており、送信が可能。
基本Django Stripe Subscriptionsを参考にする。
ERRORS:
subscriptions.StripeCustomer.user: (fields.E301) Field defines a relation with the model ‘auth.User’, which has been swapped out.
HINT: Update the relation to point at ‘settings.AUTH_USER_MODEL’.
参照サイトではDjango modelの仕様を前提としているのでエラーが出る
UserModelを使用する場合models.pyの記述方法を調整する必要がある。
subscriptions/models.pyに以下を記述する。
1 2 3 4 5 6 7 8 9 10 11 |
from django.conf import settings from django.db import models class StripeCustomer(models.Model): user = models.OneToOneField(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE) stripeCustomerId = models.CharField(max_length=255) stripeSubscriptionId = models.CharField(max_length=255) def __str__(self): return self.user.username |
参照サイトではホームページ自体がStripeペイメントのページなので、
別のURLを設定する場合subscriptions/view.pyのurlを確認する
例えば以下の場合
success_url=domain_url + ‘success?session_id=
cancel_url=domain_url + ‘cancel/’,
以下に変更する
success_url=domain_url + ‘subscriptions/success?session_id=
cancel_url=domain_url + ‘subscriptions/cancel/’,
Stripe用のモデル(データベース)が設定されていない可能性がある。
python manage.py makemigrations subscriptions
python manage.py migrate subscriptions
を行う
参照サイトではDjango modelの仕様を前提としているので
webhookでuser名の取得ができない。
user = User.objects.get(id=client_reference_id)にエラーが出る
カスタムユーザーモデルの場合上記のコードが動かないので
以下のモジュールをインポートしてコード内容をget_user_model()に変更する
from django.contrib.auth import get_user_model #インポートする
user = get_user_model().objects.get(id=client_reference_id)
ソース
ListViewにおいてStripeで支払った人にのみList内容を見せる場合で、
ListViewクラスの関数の引数がselfとなっている場合
stripe_customer = StripeCustomer.objects.get(user=request.user)で
上記エラーが出る。
その為”self.request.user”にする必要がある
stripe_customer = StripeCustomer.objects.get(user=self.request.user)
subscriptions/view.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
@login_required def cancel_subscription(request): if request.user.is_authenticated: stripe_customer = StripeCustomer.objects.get(user=request.user) stripe.api_key = settings.STRIPE_SECRET_KEY sub_id = stripe.Subscription.retrieve(stripe_customer.stripeSubscriptionId) print(sub_id) print(sub_id.id) user=request.user print("user is") print(user) try: #delete from stripeapi stripe.Subscription.delete(sub_id) #delete from StripeCustomer model StripeCustomer.objects.filter(stripeSubscriptionId=sub_id.id).delete() print('unsubscribed') except Exception as e: import traceback traceback.print_exc() return JsonResponse({'error': (e.args[0])}, status =403) return render(request, 'home.html') |
1 2 3 4 5 |
session = event['data']['object'] stripe_customer_id = session.get('customer') retrieve = stripe.Customer.retrieve(stripe_customer_id) print(retrieve['email']) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#メールアドレスの取得(その他名前などの追加情報も取得可) retrieve = stripe.Customer.retrieve(stripe_customer_id) print(retrieve['email']) customer_email = retrieve['email'] subject = "Thank you for your subscription." message = "Here is your products" from_email = 'kawazoe@customslegaloffice.com' admin_email = ['kawazoe@customslegaloffice.com',customer_email ] print("send") try: send_mail(subject ,message, from_email, admin_email) except: import traceback traceback.print_exc() |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
stripe.api_key = settings.STRIPE_SECRET_KEY endpoint_secret = settings.STRIPE_ENDPOINT_SECRET payload = request.body sig_header = request.META['HTTP_STRIPE_SIGNATURE'] event = None try: event = stripe.Webhook.construct_event( payload, sig_header, endpoint_secret ) print(event) #以下取得できるデータ一覧 { "api_version": "2020-08-27", "created": 1623290218, "data": { "object": { "allow_promotion_codes": null, "amount_subtotal": 4, "amount_total": 4, "billing_address_collection": null, "cancel_url": "http://3.129.28.xxx/subscriptions/cancel/", "client_reference_id": "1", "currency": "jpy", "customer": "cus_Jduyyr093qeu2V", "customer_details": { "email": null, "tax_exempt": "none", "tax_ids": [] }, "customer_email": null, "id": "cs_test_a1eysaqK9Evwz4U85W30sPUo9COWcG96Akw5f3CnSKgmfT7qqVwijZixxx", "livemode": false, "locale": null, "metadata": {}, "mode": "subscription", "object": "checkout.session", "payment_intent": null, "payment_method_options": {}, "payment_method_types": [ "card" ], "payment_status": "paid", "setup_intent": "seti_1J0d8eJvSJBZN02Kwwzupxxx", "shipping": null, "shipping_address_collection": null, "submit_type": null, "subscription": "sub_JduyaYhZ5UrRn1", "success_url": "http://3.129.28.xxx/subscriptions/success?session_id={CHECKOUT_SESSION_ID}", "total_details": { "amount_discount": 0, "amount_shipping": 0, "amount_tax": 0 } } }, "id": "evt_1J0d8gJvSJBZN02KkHPwAxxx", "livemode": false, "object": "event", "pending_webhooks": 3, "request": { "id": null, "idempotency_key": null }, "type": "checkout.session.completed" } |
client_reference_id,customer,subscriptionの3つのデータが欲しい場合
1 2 3 4 5 6 |
session = event['data']['object'] # Fetch all the required data from session client_reference_id = session.get('client_reference_id') stripe_customer_id = session.get('customer') stripe_subscription_id = session.get('subscription') |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
session = event['data']['object'] stripe_customer_id = session.get('customer') retrieve = stripe.Customer.retrieve(stripe_customer_id) #以下取得できるデータ一覧 { "address": { "city": null, "country": "JP", "line1": null, "line2": null, "postal_code": null, "state": null }, "balance": 4, "created": 1623314935, "currency": "jpy", "default_source": null, "delinquent": false, "description": null, "discount": null, "email": "xxxxxxxx@gmail.com", "id": "cus_Je1cRQy1VsbQrb", "invoice_prefix": "77146EAE", "invoice_settings": { "custom_fields": null, "default_payment_method": null, "footer": null }, "livemode": false, "metadata": {}, "name": "xxxxxx KAWAZOE", "next_invoice_sequence": 2, "object": "customer", "phone": null, "preferred_locales": [], "shipping": null, "tax_exempt": "none" } |
1 2 3 4 5 6 |
#メールアドレスを取得したい場合 customer_email = retrieve['email'] #価格の取得 currency = retrieve['currency'] amount = retrieve['balance'] |
※但しview内にて取得する方法は不明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
stripe_customer = StripeCustomer.objects.get(user=request.user) stripe.api_key = settings.STRIPE_SECRET_KEY sub_id = stripe.Subscription.retrieve(stripe_customer.stripeSubscriptionId) print(sub_id) #以下取得できるデータ一覧 { "application_fee_percent": null, "billing_cycle_anchor": 1623372697, "billing_thresholds": null, "cancel_at": null, "cancel_at_period_end": false, "canceled_at": null, "collection_method": "charge_automatically", "created": 1623372697, "current_period_end": 1625964697, "current_period_start": 1623372697, "customer": "cus_JeH97oQYVT2Ehd", "days_until_due": null, "default_payment_method": "pm_1J0yaxJvSJBZN02Kb7nL7O37", "default_source": null, "default_tax_rates": [], "discount": null, "ended_at": null, "id": "sub_JeH9mHHGs3SrM2", "items": { "data": [ { "billing_thresholds": null, "created": 1623372697, "id": "si_JeH9r5JrZtzGZS", "metadata": {}, "object": "subscription_item", "plan": { "active": true, "aggregate_usage": null, "amount": 4, "amount_decimal": "4", "billing_scheme": "per_unit", "created": 1623289653, "currency": "jpy", "id": "price_1J0czZJvSJBZN02KVdyeJljf", "interval": "month", "interval_count": 1, "livemode": false, "metadata": {}, "nickname": null, "object": "plan", "product": "prod_Jb1LklwtRAVkQB", "tiers_mode": null, "transform_usage": null, "trial_period_days": null, "usage_type": "licensed" }, "price": { "active": true, "billing_scheme": "per_unit", "created": 1623289653, "currency": "jpy", "id": "price_1J0czZJvSJBZN02KVdyeJljf", "livemode": false, "lookup_key": null, "metadata": {}, "nickname": null, "object": "price", "product": "prod_Jb1LklwtRAVkQB", "recurring": { "aggregate_usage": null, "interval": "month", "interval_count": 1, "trial_period_days": null, "usage_type": "licensed" }, "tiers_mode": null, "transform_quantity": null, "type": "recurring", "unit_amount": 4, "unit_amount_decimal": "4" }, "quantity": 1, "subscription": "sub_JeH9mHHGs3SrM2", "tax_rates": [] } ], "has_more": false, "object": "list", "total_count": 1, "url": "/v1/subscription_items?subscription=sub_JeH9mHHGs3SrM2" }, "latest_invoice": "in_1J0yazJvSJBZN02K4zKW5kTn", "livemode": false, "metadata": {}, "next_pending_invoice_item_invoice": null, "object": "subscription", "pause_collection": null, "pending_invoice_item_interval": null, "pending_setup_intent": null, "pending_update": null, "plan": { "active": true, "aggregate_usage": null, "amount": 4, "amount_decimal": "4", "billing_scheme": "per_unit", "created": 1623289653, "currency": "jpy", "id": "price_1J0czZJvSJBZN02KVdyeJljf", "interval": "month", "interval_count": 1, "livemode": false, "metadata": {}, "nickname": null, "object": "plan", "product": "prod_Jb1LklwtRAVkQB", "tiers_mode": null, "transform_usage": null, "trial_period_days": null, "usage_type": "licensed" }, "quantity": 1, "schedule": null, "start_date": 1623372697, "status": "active", "transfer_data": null, "trial_end": null, "trial_start": null } |
view.py
1 2 3 4 5 6 7 8 |
stripe_customer = StripeCustomer.objects.get(user=request.user) stripe.api_key = settings.STRIPE_SECRET_KEY subscription = stripe.Subscription.retrieve(stripe_customer.stripeSubscriptionId) return render(request, 'home.html',{ 'subscription': subscription, 'product': product, }) |
html
1 2 |
ProductDescription:{ product.description} Price:{ subscription.plan.currency}} {{ subscription.plan.amount}} |
exeファイルをクリックしてもThis is a command line tool.
You need to open cmd.exe and run it from there.と出て消えてしまうので
exeファイルをShiftを押しながら右クリックする事で開く事ができる。
↓コツはカーソルをstripeから離れた白地の部分でShiftを押しながら
右クリックする事。
Stripeを選んだまま右クリックしてもPowershellウィンドウを開く事は
できない
windowsの場合ログインのコマンドは以下の通り。(stripe loginだけではだめ)
1 |
./stripe login |
1 |
./stripe listen --forward-to localhost:8000/subscriptions/webhook/ |