diff --git a/Pipfile.lock b/Pipfile.lock
index 53e6e997..a4832918 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -224,11 +224,11 @@
},
"django-pagarme": {
"hashes": [
- "sha256:2afdc7a515bde810b625dab6f5a19b2a90c428e71b1be882e8485b06d36386d1",
- "sha256:fec385a79c36e3172500f8e714c234f5562207116a936d7024afebf2645c42e5"
+ "sha256:0db3dd97fbca1a2abf286bd2bce3933f5ee6471a5233b6beabab061778072a01",
+ "sha256:adff29f3fc8cece4d07ca0eae27a7454273876dd6a7531c889c0db95eacfddb1"
],
"index": "pypi",
- "version": "==0.3"
+ "version": "==0.4"
},
"django-phonenumber-field": {
"extras": [
diff --git a/pythonpro/checkout/templates/django_pagarme/show_boleto_data.html b/pythonpro/checkout/templates/django_pagarme/show_boleto_data.html
new file mode 100644
index 00000000..e710411f
--- /dev/null
+++ b/pythonpro/checkout/templates/django_pagarme/show_boleto_data.html
@@ -0,0 +1,21 @@
+{% extends 'core/base_without_nav.html' %}
+{% load django_pagarme %}
+{% block title %}Dados do Boleto{% endblock %}
+{% block description %}Dados de Boleto para Pagamento{% endblock %}
+{% block head %}
+{% endblock head %}
+
+{% block body %}
+
+{% endblock body %}
+
+{% block footer %}{% endblock footer %}
\ No newline at end of file
diff --git a/pythonpro/core/facade.py b/pythonpro/core/facade.py
index 64da9c56..c50dc8f9 100644
--- a/pythonpro/core/facade.py
+++ b/pythonpro/core/facade.py
@@ -222,3 +222,11 @@ def subscribe_to_waiting_list(user: User, source: str):
def is_client(user: User):
return has_role(user, 'client')
+
+
+def is_lead(user: User):
+ return has_role(user, 'lead')
+
+
+def is_member(user: User):
+ return has_role(user, 'member')
diff --git a/pythonpro/domain/checkout_domain.py b/pythonpro/domain/checkout_domain.py
index 6cd91aa7..92039b77 100644
--- a/pythonpro/domain/checkout_domain.py
+++ b/pythonpro/domain/checkout_domain.py
@@ -1,7 +1,9 @@
# Setup django pagarme listener
+from celery import shared_task
from django_pagarme import facade as django_pagarme_facade
from pythonpro.core import facade as core_facade
+from pythonpro.domain import user_facade
from pythonpro.email_marketing import facade as email_marketing_facade
@@ -20,3 +22,34 @@ def contact_info_listener(name, email, phone, payment_item_slug, user=None):
django_pagarme_facade.add_contact_info_listener(contact_info_listener)
+
+
+def user_factory(pagarme_transaction):
+ customer = pagarme_transaction['customer']
+ customer_email = customer['email'].lower()
+ customer_first_name = customer['name'].split()[0]
+ return user_facade.force_register_lead(customer_first_name, customer_email)
+
+
+django_pagarme_facade.set_user_factory(user_factory)
+
+
+@shared_task()
+def payment_handler_task(payment_id):
+ payment = django_pagarme_facade.find_payment(payment_id)
+ status = payment.status()
+ if status == django_pagarme_facade.PAID:
+ slug = payment.first_item_slug()
+ if 'pytools' in slug:
+ user_facade.promote_client(payment.user, 'unknow')
+ elif 'membership' in slug:
+ user_facade.promote_member(payment.user, 'unknow')
+ else:
+ raise ValueError(f'{slug} should contain pytools or membership')
+
+
+def payment_change_handler(payment_id):
+ payment_handler_task.delay(payment_id)
+
+
+django_pagarme_facade.add_payment_status_changed(payment_change_handler)
diff --git a/pythonpro/domain/tests/test_checkout/conftest.py b/pythonpro/domain/tests/test_checkout/conftest.py
index 16fdb522..8b7bf89d 100644
--- a/pythonpro/domain/tests/test_checkout/conftest.py
+++ b/pythonpro/domain/tests/test_checkout/conftest.py
@@ -1,9 +1,11 @@
from importlib import import_module
import pytest
+from django_pagarme import facade
from django_pagarme.models import PagarmeFormConfig, PagarmeItemConfig
# Workaround since module beginning with number can't be imported in regular way
+
migration_module = import_module('pythonpro.checkout.migrations.0001_payment_setup')
@@ -11,3 +13,26 @@
def execute_migration(db, pytestconfig):
if pytestconfig.known_args_namespace.nomigrations:
migration_module.setup_payment_configs_function(PagarmeFormConfig, PagarmeItemConfig)
+
+
+@pytest.fixture(autouse=True)
+def disable_email_marketing(settings):
+ settings.ACTIVE_CAMPAIGN_TURNED_ON = False
+
+
+@pytest.fixture(autouse=True)
+def disable_forum_integration(settings):
+ settings.DISCOURSE_BASE_URL = ''
+ settings.DISCOURSE_SSO_SECRET = ''
+ settings.DISCOURSE_API_KEY = ''
+ settings.DISCOURSE_API_USER = ''
+
+
+@pytest.fixture
+def pytools_item(execute_migration):
+ return facade.find_payment_item_config('pytools')
+
+
+@pytest.fixture
+def membership_item(execute_migration, cohort):
+ return facade.find_payment_item_config('membership')
diff --git a/pythonpro/domain/tests/test_checkout/test_boleto_generation.py b/pythonpro/domain/tests/test_checkout/test_boleto_generation.py
new file mode 100644
index 00000000..0d222b84
--- /dev/null
+++ b/pythonpro/domain/tests/test_checkout/test_boleto_generation.py
@@ -0,0 +1,309 @@
+import pytest
+import responses
+from django.urls import reverse
+from django_pagarme import facade as django_pagarme_facade
+
+from pythonpro.core import facade as core_facade
+from pythonpro.domain import checkout_domain
+from pythonpro.email_marketing import facade as email_marketing_facade
+
+
+@pytest.fixture
+def pagarme_responses(transaction_json, captura_json):
+ with responses.RequestsMock() as rsps:
+ rsps.add(responses.GET, f'https://api.pagar.me/1/transactions/{TOKEN}', json=transaction_json)
+ rsps.add(responses.POST, f'https://api.pagar.me/1/transactions/{TOKEN}/capture', json=captura_json)
+ yield rsps
+
+
+TRANSACTION_ID = 7656690
+TOKEN = 'test_transaction_aJx9ibUmRqYcQrrUaNtQ3arTO4tF1z'
+BOLETO_URL = 'www.some.boleto.com'
+BOLETO_BARCODE = '123455'
+
+
+# test user not logged
+@pytest.fixture
+def create_or_update_lead_mock(mocker):
+ return mocker.patch(
+ 'pythonpro.domain.user_facade._email_marketing_facade.create_or_update_lead.delay',
+ side_effect=email_marketing_facade.create_or_update_lead
+ )
+
+
+@pytest.fixture
+def payment_handler_task_mock(mocker):
+ return mocker.patch(
+ 'pythonpro.domain.checkout_domain.payment_handler_task.delay',
+ side_effect=checkout_domain.payment_handler_task
+ )
+
+
+@pytest.fixture
+def resp(client, pagarme_responses, create_or_update_lead_mock, payment_handler_task_mock):
+ return client.get(reverse('django_pagarme:capture', kwargs={'token': TOKEN}), secure=True)
+
+
+def test_status_code(resp):
+ assert resp.status_code == 200
+
+
+def test_user_is_created(resp, django_user_model):
+ User = django_user_model
+ assert User.objects.exists()
+
+
+def test_user_is_lead(resp, django_user_model):
+ User = django_user_model
+ user = User.objects.first()
+ assert core_facade.is_lead(user)
+
+
+def test_payment_linked_with_created_user(resp, django_user_model):
+ User = django_user_model
+ user = User.objects.first()
+ payment = django_pagarme_facade.find_payment_by_transaction(TRANSACTION_ID)
+ assert user == payment.user
+
+
+# Tests user logged
+
+@pytest.fixture
+def resp_logged_user(client_with_lead, pagarme_responses, payment_handler_task_mock):
+ return client_with_lead.get(reverse('django_pagarme:capture', kwargs={'token': TOKEN}), secure=True)
+
+
+def test_logged_user_become_lead(resp_logged_user, logged_user):
+ assert core_facade.is_lead(logged_user)
+
+
+def test_payment_linked_with_logged_user(resp_logged_user, logged_user):
+ payment = django_pagarme_facade.find_payment_by_transaction(TRANSACTION_ID)
+ assert logged_user == payment.user
+
+
+@pytest.fixture
+def transaction_json(pytools_item):
+ return {
+ 'object': 'transaction',
+ 'status': 'authorized',
+ 'refuse_reason': None,
+ 'status_reason': 'acquirer',
+ 'acquirer_response_code': None,
+ 'acquirer_name': 'pagarme',
+ 'acquirer_id': '5cdec7071458b442125d940b',
+ 'authorization_code': None,
+ 'soft_descriptor': None,
+ 'tid': TRANSACTION_ID,
+ 'nsu': TRANSACTION_ID,
+ 'date_created': '2020-03-07T17:04:58.279Z',
+ 'date_updated': '2020-03-07T17:04:58.502Z',
+ 'authorized_amount': pytools_item.price,
+ 'paid_amount': 0,
+ 'refunded_amount': 0,
+ 'installments': 1,
+ 'id': TRANSACTION_ID,
+ 'cost': 0,
+ 'card_holder_name': None,
+ 'card_last_digits': None,
+ 'card_first_digits': None,
+ 'card_brand': None,
+ 'card_pin_mode': None,
+ 'card_magstripe_fallback': False,
+ 'cvm_pin': False,
+ 'postback_url': 'https://e0f89dca.ngrok.io/django_pagarme/notification',
+ 'payment_method': 'boleto',
+ 'capture_method': 'ecommerce',
+ 'antifraud_score': None,
+ 'boleto_url': None,
+ 'boleto_barcode': None,
+ 'boleto_expiration_date': '2020-03-09T03:00:00.000Z',
+ 'referer': 'encryption_key',
+ 'ip': '177.170.213.5',
+ 'subscription_id': None,
+ 'phone': None,
+ 'address': None,
+ 'customer': {
+ 'object': 'customer',
+ 'id': 2725813,
+ 'external_id': 'foo@email.com',
+ 'type': 'individual',
+ 'country': 'br',
+ 'document_number': None,
+ 'document_type': 'cpf',
+ 'name': 'Foo',
+ 'email': 'foo@email.com',
+ 'phone_numbers': ['+5512999999999'],
+ 'born_at': None,
+ 'birthday': None,
+ 'gender': None,
+ 'date_created': '2020-03-07T17:04:58.220Z',
+ 'documents': [
+ {
+ 'object': 'document',
+ 'id': 'doc_ck7huyv07072mmp6f59af8u8h',
+ 'type': 'cpf',
+ 'number': '04367331024'
+ }]
+ },
+ 'billing': {
+ 'object': 'billing',
+ 'id': 1168861,
+ 'name': 'Foo',
+ 'address': {
+ 'object': 'address',
+ 'street': 'Rua Buenos Aires',
+ 'complementary': 'Sem complemento',
+ 'street_number': '7',
+ 'neighborhood': 'Cidade Vista Verde',
+ 'city': 'São José dos Campos',
+ 'state': 'SP',
+ 'zipcode': '12223730',
+ 'country': 'br',
+ 'id': 2641028
+ }
+ },
+ 'shipping': None,
+ 'items': [{
+ 'object': 'item',
+ 'id': f'{pytools_item.slug}',
+ 'title': f'{pytools_item.name}',
+ 'unit_price': pytools_item.price,
+ 'quantity': 1,
+ 'category': None,
+ 'tangible': False,
+ 'venue': None,
+ 'date': None
+ }],
+ 'card': None,
+ 'split_rules': None,
+ 'metadata': {},
+ 'antifraud_metadata': {},
+ 'reference_key': None,
+ 'device': None,
+ 'local_transaction_id': None,
+ 'local_time': None,
+ 'fraud_covered': False,
+ 'fraud_reimbursed': None,
+ 'order_id': None,
+ 'risk_level': 'unknown',
+ 'receipt_url': None,
+ 'payment': None,
+ 'addition': None,
+ 'discount': None,
+ 'private_label': None
+ }
+
+
+@pytest.fixture
+def captura_json(pytools_item):
+ return {
+ 'object': 'transaction',
+ 'status': 'waiting_payment',
+ 'refuse_reason': None,
+ 'status_reason': 'acquirer',
+ 'acquirer_response_code': None,
+ 'acquirer_name': 'pagarme',
+ 'acquirer_id': '5cdec7071458b442125d940b',
+ 'authorization_code': None,
+ 'soft_descriptor': None,
+ 'tid': TRANSACTION_ID,
+ 'nsu': TRANSACTION_ID,
+ 'date_created': '2020-03-07T17:04:58.279Z',
+ 'date_updated': '2020-03-07T17:11:14.957Z',
+ 'amount': pytools_item.price,
+ 'authorized_amount': pytools_item.price,
+ 'paid_amount': 0,
+ 'refunded_amount': 0,
+ 'installments': 1,
+ 'id': TRANSACTION_ID,
+ 'cost': 0,
+ 'card_holder_name': None,
+ 'card_last_digits': None,
+ 'card_first_digits': None,
+ 'card_brand': None,
+ 'card_pin_mode': None,
+ 'card_magstripe_fallback': False,
+ 'cvm_pin': False,
+ 'postback_url': 'https://e0f89dca.ngrok.io/django_pagarme/notification',
+ 'payment_method': 'boleto',
+ 'capture_method': 'ecommerce',
+ 'antifraud_score': None,
+ 'boleto_url': BOLETO_URL,
+ 'boleto_barcode': BOLETO_BARCODE,
+ 'boleto_expiration_date': '2020-03-09T03:00:00.000Z',
+ 'referer': 'encryption_key',
+ 'ip': '177.170.213.5',
+ 'subscription_id': None,
+ 'phone': None,
+ 'address': None,
+ 'customer': {
+ 'object': 'customer',
+ 'id': 2725813,
+ 'external_id': 'foo@email.com',
+ 'type': 'individual',
+ 'country': 'br',
+ 'document_number': None,
+ 'document_type': 'cpf',
+ 'name': 'Foo',
+ 'email': 'foo@email.com',
+ 'phone_numbers': ['+5512999999999'],
+ 'born_at': None,
+ 'birthday': None,
+ 'gender': None,
+ 'date_created': '2020-03-07T17:04:58.220Z',
+ 'documents': [
+ {
+ 'object': 'document',
+ 'id': 'doc_ck7huyv07072mmp6f59af8u8h',
+ 'type': 'cpf',
+ 'number': '04367331024'
+ }]
+ },
+ 'billing': {
+ 'object': 'billing',
+ 'id': 1168861,
+ 'name': 'Foo',
+ 'address': {
+ 'object': 'address',
+ 'street': 'Rua Buenos Aires',
+ 'complementary': 'Sem complemento',
+ 'street_number': '7',
+ 'neighborhood': 'Cidade Vista Verde',
+ 'city': 'São José dos Campos',
+ 'state': 'SP',
+ 'zipcode': '12223730',
+ 'country': 'br',
+ 'id': 2641028
+ }
+ },
+ 'shipping': None,
+ 'items': [{
+ 'object': 'item',
+ 'id': f'{pytools_item.slug}',
+ 'title': f'{pytools_item.name}',
+ 'unit_price': pytools_item.price,
+ 'quantity': 1,
+ 'category': None,
+ 'tangible': False,
+ 'venue': None,
+ 'date': None
+ }],
+ 'card': None,
+ 'split_rules': None,
+ 'metadata': {},
+ 'antifraud_metadata': {},
+ 'reference_key': None,
+ 'device': None,
+ 'local_transaction_id': None,
+ 'local_time': None,
+ 'fraud_covered': False,
+ 'fraud_reimbursed': None,
+ 'order_id': None,
+ 'risk_level': 'unknown',
+ 'receipt_url': None,
+ 'payment': None,
+ 'addition': None,
+ 'discount': None,
+ 'private_label': None
+ }
diff --git a/pythonpro/domain/tests/test_checkout/test_credit_card_payment_client.py b/pythonpro/domain/tests/test_checkout/test_credit_card_payment_client.py
new file mode 100644
index 00000000..dca9b92b
--- /dev/null
+++ b/pythonpro/domain/tests/test_checkout/test_credit_card_payment_client.py
@@ -0,0 +1,170 @@
+import pytest
+import responses
+from django.urls import reverse
+from django_pagarme import facade as django_pagarme_facade
+
+from pythonpro.core import facade as core_facade
+from pythonpro.domain import checkout_domain
+from pythonpro.email_marketing import facade as email_marketing_facade
+
+
+@pytest.fixture
+def pagarme_responses(transaction_json, captura_json):
+ with responses.RequestsMock() as rsps:
+ rsps.add(responses.GET, f'https://api.pagar.me/1/transactions/{TOKEN}', json=transaction_json)
+ rsps.add(responses.POST, f'https://api.pagar.me/1/transactions/{TOKEN}/capture', json=captura_json)
+ yield rsps
+
+
+TRANSACTION_ID = 7656690
+TOKEN = 'test_transaction_aJx9ibUmRqYcQrrUaNtQ3arTO4tF1z'
+
+
+@pytest.fixture
+def create_or_update_client_mock(mocker):
+ return mocker.patch(
+ 'pythonpro.domain.user_facade._email_marketing_facade.create_or_update_client.delay',
+ side_effect=email_marketing_facade.create_or_update_client
+ )
+
+
+@pytest.fixture
+def create_or_update_lead_mock(mocker):
+ return mocker.patch(
+ 'pythonpro.domain.user_facade._email_marketing_facade.create_or_update_lead.delay',
+ side_effect=email_marketing_facade.create_or_update_lead
+ )
+
+
+@pytest.fixture
+def payment_handler_task_mock(mocker):
+ return mocker.patch(
+ 'pythonpro.domain.checkout_domain.payment_handler_task.delay',
+ side_effect=checkout_domain.payment_handler_task
+ )
+
+
+# test user not logged
+@pytest.fixture
+def resp(client, pagarme_responses, payment_handler_task_mock, create_or_update_client_mock,
+ create_or_update_lead_mock):
+ return client.get(reverse('django_pagarme:capture', kwargs={'token': TOKEN}), secure=True)
+
+
+def test_status_code(resp, pytools_item, payment_handler_task_mock, create_or_update_client_mock):
+ assert resp.status_code == 302
+ assert resp.url == reverse('django_pagarme:thanks', kwargs={'slug': pytools_item.slug})
+
+
+def test_user_is_created(resp, django_user_model):
+ User = django_user_model
+ assert User.objects.exists()
+
+
+def test_user_is_client(resp, django_user_model):
+ User = django_user_model
+ user = User.objects.first()
+ assert core_facade.is_client(user)
+
+
+def test_payment_linked_with_created_user(resp, django_user_model):
+ User = django_user_model
+ user = User.objects.first()
+ payment = django_pagarme_facade.find_payment_by_transaction(TRANSACTION_ID)
+ assert user == payment.user
+
+
+# Tests user logged
+
+@pytest.fixture
+def resp_logged_user(client_with_user, pagarme_responses, payment_handler_task_mock):
+ return client_with_user.get(reverse('django_pagarme:capture', kwargs={'token': TOKEN}), secure=True)
+
+
+def test_logged_user_become_client(resp_logged_user, logged_user):
+ assert core_facade.is_client(logged_user)
+
+
+def test_payment_linked_with_logged_user(resp_logged_user, logged_user):
+ payment = django_pagarme_facade.find_payment_by_transaction(TRANSACTION_ID)
+ assert logged_user == payment.user
+
+
+@pytest.fixture
+def transaction_json(pytools_item):
+ return {
+ 'object': 'transaction', 'status': 'authorized', 'refuse_reason': None, 'status_reason': 'antifraud',
+ 'acquirer_response_code': '0000', 'acquirer_name': 'pagarme', 'acquirer_id': '5cdec7071458b442125d940b',
+ 'authorization_code': '727706', 'soft_descriptor': None, 'tid': TRANSACTION_ID, 'nsu': TRANSACTION_ID,
+ 'date_created': '2020-01-21T01:10:13.015Z', 'date_updated': '2020-01-21T01:10:13.339Z',
+ 'amount': pytools_item.price,
+ 'authorized_amount': pytools_item.price, 'paid_amount': 0, 'refunded_amount': 0, 'installments': 1,
+ 'id': TRANSACTION_ID, 'cost': 70,
+ 'card_holder_name': 'Bar Baz', 'card_last_digits': '1111', 'card_first_digits': '411111', 'card_brand': 'visa',
+ 'card_pin_mode': None, 'card_magstripe_fallback': False, 'cvm_pin': False, 'postback_url': None,
+ 'payment_method': 'credit_card', 'capture_method': 'ecommerce', 'antifraud_score': None, 'boleto_url': None,
+ 'boleto_barcode': None, 'boleto_expiration_date': None, 'referer': 'encryption_key', 'ip': '177.27.238.139',
+ 'items': [{
+ 'object': 'item',
+ 'id': f'{pytools_item.slug}',
+ 'title': f'{pytools_item.name}',
+ 'unit_price': pytools_item.price,
+ 'quantity': 1, 'category': None, 'tangible': False, 'venue': None, 'date': None
+ }], 'card': {
+ 'object': 'card', 'id': 'card_ck5n7vtbi010or36dojq96sb1', 'date_created': '2020-01-21T01:45:57.294Z',
+ 'date_updated': '2020-01-21T01:45:57.789Z', 'brand': 'visa', 'holder_name': 'agora captura',
+ 'first_digits': '411111', 'last_digits': '1111', 'country': 'UNITED STATES',
+ 'fingerprint': 'cj5bw4cio00000j23jx5l60cq', 'valid': True, 'expiration_date': '1227'
+ }
+ }
+
+
+@pytest.fixture
+def captura_json(pytools_item):
+ return {
+ 'object': 'transaction', 'status': 'paid', 'refuse_reason': None, 'status_reason': 'acquirer',
+ 'acquirer_response_code': '0000', 'acquirer_name': 'pagarme', 'acquirer_id': '5cdec7071458b442125d940b',
+ 'authorization_code': '408324', 'soft_descriptor': None, 'tid': TRANSACTION_ID, 'nsu': TRANSACTION_ID,
+ 'date_created': '2020-01-21T01:45:57.309Z', 'date_updated': '2020-01-21T01:47:27.105Z', 'amount': 8000,
+ 'authorized_amount': pytools_item.price,
+ 'paid_amount': pytools_item.price, 'refunded_amount': 0,
+ 'installments': 1,
+ 'id': TRANSACTION_ID,
+ 'cost': 100,
+ 'card_holder_name': 'agora captura', 'card_last_digits': '1111', 'card_first_digits': '411111',
+ 'card_brand': 'visa', 'card_pin_mode': None, 'card_magstripe_fallback': False, 'cvm_pin': False,
+ 'postback_url': None,
+ 'payment_method': 'credit_card', 'capture_method': 'ecommerce', 'antifraud_score': None,
+ 'boleto_url': None, 'boleto_barcode': None, 'boleto_expiration_date': None, 'referer': 'encryption_key',
+ 'ip': '177.27.238.139', 'subscription_id': None, 'phone': None, 'address': None,
+ 'customer': {
+ 'object': 'customer', 'id': 2601905, 'external_id': 'captura@gmail.com', 'type': 'individual',
+ 'country': 'br',
+ 'document_number': None, 'document_type': 'cpf', 'name': 'Agora Captura', 'email': 'captura@gmail.com',
+ 'phone_numbers': ['+5512997411854'], 'born_at': None, 'birthday': None, 'gender': None,
+ 'date_created': '2020-01-21T01:45:57.228Z', 'documents': [
+ {'object': 'document', 'id': 'doc_ck5n7vta0010nr36d01k1zzzw', 'type': 'cpf', 'number': '29770166863'}]
+ }, 'billing': {
+ 'object': 'billing', 'id': 1135539, 'name': 'Agora Captura', 'address': {
+ 'object': 'address', 'street': 'Rua Bahamas', 'complementary': 'Sem complemento', 'street_number': '56',
+ 'neighborhood': 'Cidade Vista Verde', 'city': 'São José dos Campos', 'state': 'SP',
+ 'zipcode': '12223770',
+ 'country': 'br', 'id': 2559019
+ }
+ }, 'shipping': None,
+ 'items': [{
+ 'object': 'item',
+ 'id': f'{pytools_item.slug}',
+ 'title': f'{pytools_item.name}',
+ 'unit_price': pytools_item.price,
+ 'quantity': 1, 'category': None, 'tangible': False, 'venue': None, 'date': None
+ }], 'card': {
+ 'object': 'card', 'id': 'card_ck5n7vtbi010or36dojq96sb1', 'date_created': '2020-01-21T01:45:57.294Z',
+ 'date_updated': '2020-01-21T01:45:57.789Z', 'brand': 'visa', 'holder_name': 'agora captura',
+ 'first_digits': '411111', 'last_digits': '1111', 'country': 'UNITED STATES',
+ 'fingerprint': 'cj5bw4cio00000j23jx5l60cq', 'valid': True, 'expiration_date': '1227'
+ }, 'split_rules': None, 'metadata': {}, 'antifraud_metadata': {}, 'reference_key': None, 'device': None,
+ 'local_transaction_id': None, 'local_time': None, 'fraud_covered': False, 'fraud_reimbursed': None,
+ 'order_id': None, 'risk_level': 'very_low', 'receipt_url': None, 'payment': None, 'addition': None,
+ 'discount': None, 'private_label': None
+ }
diff --git a/pythonpro/domain/tests/test_checkout/test_credit_card_payment_member.py b/pythonpro/domain/tests/test_checkout/test_credit_card_payment_member.py
new file mode 100644
index 00000000..22761237
--- /dev/null
+++ b/pythonpro/domain/tests/test_checkout/test_credit_card_payment_member.py
@@ -0,0 +1,181 @@
+import pytest
+import responses
+from django.urls import reverse
+from django_pagarme import facade as django_pagarme_facade
+
+from pythonpro.core import facade as core_facade
+from pythonpro.domain import checkout_domain
+from pythonpro.email_marketing import facade as email_marketing_facade
+
+
+@pytest.fixture
+def pagarme_responses(transaction_json, captura_json):
+ with responses.RequestsMock() as rsps:
+ rsps.add(responses.GET, f'https://api.pagar.me/1/transactions/{TOKEN}', json=transaction_json)
+ rsps.add(responses.POST, f'https://api.pagar.me/1/transactions/{TOKEN}/capture', json=captura_json)
+ yield rsps
+
+
+TRANSACTION_ID = 7656690
+TOKEN = 'test_transaction_aJx9ibUmRqYcQrrUaNtQ3arTO4tF1z'
+
+
+@pytest.fixture
+def create_or_update_member_mock(mocker):
+ return mocker.patch(
+ 'pythonpro.domain.user_facade._email_marketing_facade.create_or_update_member.delay',
+ side_effect=email_marketing_facade.create_or_update_member
+ )
+
+
+@pytest.fixture
+def create_or_update_lead_mock(mocker):
+ return mocker.patch(
+ 'pythonpro.domain.user_facade._email_marketing_facade.create_or_update_lead.delay',
+ side_effect=email_marketing_facade.create_or_update_lead
+ )
+
+
+@pytest.fixture
+def payment_handler_task_mock(mocker):
+ return mocker.patch(
+ 'pythonpro.domain.checkout_domain.payment_handler_task.delay',
+ side_effect=checkout_domain.payment_handler_task
+ )
+
+
+# test user not logged
+
+@pytest.fixture
+def resp(client, pagarme_responses, payment_handler_task_mock, create_or_update_lead_mock,
+ create_or_update_member_mock):
+ return client.get(reverse('django_pagarme:capture', kwargs={'token': TOKEN}), secure=True)
+
+
+def test_status_code(resp, membership_item):
+ assert resp.status_code == 302
+ assert resp.url == reverse('django_pagarme:thanks', kwargs={'slug': membership_item.slug})
+
+
+def test_user_is_created(resp, django_user_model):
+ User = django_user_model
+ assert User.objects.exists()
+
+
+def test_user_is_member(resp, django_user_model):
+ User = django_user_model
+ user = User.objects.first()
+ assert core_facade.is_member(user)
+
+
+def test_user_is_subscribed_to_cohort(resp, django_user_model, cohort):
+ User = django_user_model
+ user = User.objects.first()
+ assert cohort.students.first() == user
+
+
+def test_payment_linked_with_created_user(resp, django_user_model):
+ User = django_user_model
+ user = User.objects.first()
+ payment = django_pagarme_facade.find_payment_by_transaction(TRANSACTION_ID)
+ assert user == payment.user
+
+
+# Tests user logged
+
+@pytest.fixture
+def resp_logged_user(client_with_user, pagarme_responses, payment_handler_task_mock):
+ return client_with_user.get(reverse('django_pagarme:capture', kwargs={'token': TOKEN}), secure=True)
+
+
+def test_logged_user_become_member(resp_logged_user, logged_user):
+ assert core_facade.is_member(logged_user)
+
+
+def test_payment_linked_with_logged_user(resp_logged_user, logged_user):
+ payment = django_pagarme_facade.find_payment_by_transaction(TRANSACTION_ID)
+ assert logged_user == payment.user
+
+
+def test_logged_user_is_subscribed_to_cohort(resp_logged_user, logged_user, cohort):
+ assert cohort.students.first() == logged_user
+
+
+@pytest.fixture
+def transaction_json(membership_item):
+ return {
+ 'object': 'transaction', 'status': 'authorized', 'refuse_reason': None, 'status_reason': 'antifraud',
+ 'acquirer_response_code': '0000', 'acquirer_name': 'pagarme', 'acquirer_id': '5cdec7071458b442125d940b',
+ 'authorization_code': '727706', 'soft_descriptor': None, 'tid': TRANSACTION_ID, 'nsu': TRANSACTION_ID,
+ 'date_created': '2020-01-21T01:10:13.015Z', 'date_updated': '2020-01-21T01:10:13.339Z',
+ 'amount': membership_item.price,
+ 'authorized_amount': membership_item.price, 'paid_amount': 0, 'refunded_amount': 0, 'installments': 1,
+ 'id': TRANSACTION_ID, 'cost': 70,
+ 'card_holder_name': 'Bar Baz', 'card_last_digits': '1111', 'card_first_digits': '411111', 'card_brand': 'visa',
+ 'card_pin_mode': None, 'card_magstripe_fallback': False, 'cvm_pin': False, 'postback_url': None,
+ 'payment_method': 'credit_card', 'capture_method': 'ecommerce', 'antifraud_score': None, 'boleto_url': None,
+ 'boleto_barcode': None, 'boleto_expiration_date': None, 'referer': 'encryption_key', 'ip': '177.27.238.139',
+ 'items': [{
+ 'object': 'item',
+ 'id': f'{membership_item.slug}',
+ 'title': f'{membership_item.name}',
+ 'unit_price': membership_item.price,
+ 'quantity': 1, 'category': None, 'tangible': False, 'venue': None, 'date': None
+ }], 'card': {
+ 'object': 'card', 'id': 'card_ck5n7vtbi010or36dojq96sb1', 'date_created': '2020-01-21T01:45:57.294Z',
+ 'date_updated': '2020-01-21T01:45:57.789Z', 'brand': 'visa', 'holder_name': 'agora captura',
+ 'first_digits': '411111', 'last_digits': '1111', 'country': 'UNITED STATES',
+ 'fingerprint': 'cj5bw4cio00000j23jx5l60cq', 'valid': True, 'expiration_date': '1227'
+ }
+ }
+
+
+@pytest.fixture
+def captura_json(membership_item):
+ return {
+ 'object': 'transaction', 'status': 'paid', 'refuse_reason': None, 'status_reason': 'acquirer',
+ 'acquirer_response_code': '0000', 'acquirer_name': 'pagarme', 'acquirer_id': '5cdec7071458b442125d940b',
+ 'authorization_code': '408324', 'soft_descriptor': None, 'tid': TRANSACTION_ID, 'nsu': TRANSACTION_ID,
+ 'date_created': '2020-01-21T01:45:57.309Z', 'date_updated': '2020-01-21T01:47:27.105Z', 'amount': 8000,
+ 'authorized_amount': membership_item.price,
+ 'paid_amount': membership_item.price, 'refunded_amount': 0,
+ 'installments': 1,
+ 'id': TRANSACTION_ID,
+ 'cost': 100,
+ 'card_holder_name': 'agora captura', 'card_last_digits': '1111', 'card_first_digits': '411111',
+ 'card_brand': 'visa', 'card_pin_mode': None, 'card_magstripe_fallback': False, 'cvm_pin': False,
+ 'postback_url': None,
+ 'payment_method': 'credit_card', 'capture_method': 'ecommerce', 'antifraud_score': None,
+ 'boleto_url': None, 'boleto_barcode': None, 'boleto_expiration_date': None, 'referer': 'encryption_key',
+ 'ip': '177.27.238.139', 'subscription_id': None, 'phone': None, 'address': None,
+ 'customer': {
+ 'object': 'customer', 'id': 2601905, 'external_id': 'captura@gmail.com', 'type': 'individual',
+ 'country': 'br',
+ 'document_number': None, 'document_type': 'cpf', 'name': 'Agora Captura', 'email': 'captura@gmail.com',
+ 'phone_numbers': ['+5512997411854'], 'born_at': None, 'birthday': None, 'gender': None,
+ 'date_created': '2020-01-21T01:45:57.228Z', 'documents': [
+ {'object': 'document', 'id': 'doc_ck5n7vta0010nr36d01k1zzzw', 'type': 'cpf', 'number': '29770166863'}]
+ }, 'billing': {
+ 'object': 'billing', 'id': 1135539, 'name': 'Agora Captura', 'address': {
+ 'object': 'address', 'street': 'Rua Bahamas', 'complementary': 'Sem complemento', 'street_number': '56',
+ 'neighborhood': 'Cidade Vista Verde', 'city': 'São José dos Campos', 'state': 'SP',
+ 'zipcode': '12223770',
+ 'country': 'br', 'id': 2559019
+ }
+ }, 'shipping': None,
+ 'items': [{
+ 'object': 'item',
+ 'id': f'{membership_item.slug}',
+ 'title': f'{membership_item.name}',
+ 'unit_price': membership_item.price,
+ 'quantity': 1, 'category': None, 'tangible': False, 'venue': None, 'date': None
+ }], 'card': {
+ 'object': 'card', 'id': 'card_ck5n7vtbi010or36dojq96sb1', 'date_created': '2020-01-21T01:45:57.294Z',
+ 'date_updated': '2020-01-21T01:45:57.789Z', 'brand': 'visa', 'holder_name': 'agora captura',
+ 'first_digits': '411111', 'last_digits': '1111', 'country': 'UNITED STATES',
+ 'fingerprint': 'cj5bw4cio00000j23jx5l60cq', 'valid': True, 'expiration_date': '1227'
+ }, 'split_rules': None, 'metadata': {}, 'antifraud_metadata': {}, 'reference_key': None, 'device': None,
+ 'local_transaction_id': None, 'local_time': None, 'fraud_covered': False, 'fraud_reimbursed': None,
+ 'order_id': None, 'risk_level': 'very_low', 'receipt_url': None, 'payment': None, 'addition': None,
+ 'discount': None, 'private_label': None
+ }
diff --git a/pythonpro/email_marketing/facade.py b/pythonpro/email_marketing/facade.py
index a74d7ff4..760b21c2 100644
--- a/pythonpro/email_marketing/facade.py
+++ b/pythonpro/email_marketing/facade.py
@@ -110,6 +110,7 @@ def grant_role(email, id, role: str):
_client.contacts.remove_tag(roles_to_remove_data)
+@shared_task()
def tag_as(email: str, id: int, *tags):
if settings.ACTIVE_CAMPAIGN_TURNED_ON is False:
return