From e4873df8963cec141cd66e61358bb7c12b9618f2 Mon Sep 17 00:00:00 2001
From: Jonas Borges <jonas.borges@starkbank.com>
Date: Wed, 26 Feb 2025 16:14:08 -0300
Subject: [PATCH] Add merchant purchase, merchant session, merchant card and
 merchant installment

---
 CHANGELOG.md                                  |   2 +
 README.md                                     | 208 ++++++++++++++++++
 starkbank/__init__.py                         |  12 +
 starkbank/merchantcard/__init__.py            |   3 +
 starkbank/merchantcard/__merchantcard.py      |  55 +++++
 starkbank/merchantcard/log/__init__.py        |   1 +
 starkbank/merchantcard/log/__log.py           |  53 +++++
 starkbank/merchantinstallment/__init__.py     |   3 +
 .../__merchantinstallment.py                  |  60 +++++
 starkbank/merchantinstallment/log/__init__.py |   1 +
 starkbank/merchantinstallment/log/__log.py    |  53 +++++
 starkbank/merchantpurchase/__init__.py        |   3 +
 .../merchantpurchase/__merchantpurchase.py    |  94 ++++++++
 starkbank/merchantpurchase/log/__init__.py    |   1 +
 starkbank/merchantpurchase/log/__log.py       |  52 +++++
 starkbank/merchantsession/__init__.py         |   5 +
 .../merchantsession/__merchantsession.py      |  83 +++++++
 starkbank/merchantsession/__purchase.py       |  50 +++++
 .../__allowedinstallment.py                   |  12 +
 .../allowedinstallment/__init__.py            |   0
 starkbank/merchantsession/log/__init__.py     |   1 +
 starkbank/merchantsession/log/__log.py        |  52 +++++
 starkbank/utils/rest.py                       |   1 +
 tests/sdk/test_corporate_card_log.py          |   3 +-
 tests/sdk/test_merchant_card.py               |  41 ++++
 tests/sdk/test_merchant_card_log.py           |  45 ++++
 tests/sdk/test_merchant_installment.py        |  43 ++++
 tests/sdk/test_merchant_installment_log.py    |  45 ++++
 tests/sdk/test_merchant_purchase.py           |  66 ++++++
 tests/sdk/test_merchant_purchase_log.py       |  45 ++++
 tests/sdk/test_merchant_session.py            |  81 +++++++
 tests/sdk/test_merchant_session_log.py        |  44 ++++
 tests/utils/merchantPurchase.py               |  93 ++++++++
 tests/utils/merchantSession.py                |  84 +++++++
 34 files changed, 1394 insertions(+), 1 deletion(-)
 create mode 100644 starkbank/merchantcard/__init__.py
 create mode 100644 starkbank/merchantcard/__merchantcard.py
 create mode 100644 starkbank/merchantcard/log/__init__.py
 create mode 100644 starkbank/merchantcard/log/__log.py
 create mode 100644 starkbank/merchantinstallment/__init__.py
 create mode 100644 starkbank/merchantinstallment/__merchantinstallment.py
 create mode 100644 starkbank/merchantinstallment/log/__init__.py
 create mode 100644 starkbank/merchantinstallment/log/__log.py
 create mode 100644 starkbank/merchantpurchase/__init__.py
 create mode 100644 starkbank/merchantpurchase/__merchantpurchase.py
 create mode 100644 starkbank/merchantpurchase/log/__init__.py
 create mode 100644 starkbank/merchantpurchase/log/__log.py
 create mode 100644 starkbank/merchantsession/__init__.py
 create mode 100644 starkbank/merchantsession/__merchantsession.py
 create mode 100644 starkbank/merchantsession/__purchase.py
 create mode 100644 starkbank/merchantsession/allowedinstallment/__allowedinstallment.py
 create mode 100644 starkbank/merchantsession/allowedinstallment/__init__.py
 create mode 100644 starkbank/merchantsession/log/__init__.py
 create mode 100644 starkbank/merchantsession/log/__log.py
 create mode 100644 tests/sdk/test_merchant_card.py
 create mode 100644 tests/sdk/test_merchant_card_log.py
 create mode 100644 tests/sdk/test_merchant_installment.py
 create mode 100644 tests/sdk/test_merchant_installment_log.py
 create mode 100644 tests/sdk/test_merchant_purchase.py
 create mode 100644 tests/sdk/test_merchant_purchase_log.py
 create mode 100644 tests/sdk/test_merchant_session.py
 create mode 100644 tests/sdk/test_merchant_session_log.py
 create mode 100644 tests/utils/merchantPurchase.py
 create mode 100644 tests/utils/merchantSession.py

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4d27b467..743e1d66 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,8 @@ Given a version number MAJOR.MINOR.PATCH, increment:
 
 
 ## [Unreleased]
+### Added
+- MerchantSession and MerchantSessionPurchase resources
 
 ## [2.26.1] - 2025-02-18
 ### Fixed
diff --git a/README.md b/README.md
index 28692264..25e5d15f 100644
--- a/README.md
+++ b/README.md
@@ -49,6 +49,10 @@ is as easy as sending a text message to your client!
     - [CorporateBalance](#get-your-corporatebalance): View your corporate balance
     - [CorporateTransactions](#query-corporatetransactions): View the transactions that have affected your corporate balance
     - [CorporateEnums](#corporate-enums): Query enums related to the corporate purchases, such as merchant categories, countries and card purchase methods
+    - [MerchantCard](#query-merchantcards): Stores information about approved purchase cards for reuse
+    - [MerchantSession](#create-a-merchantsession): Manages a session to create a purchase with a new card
+    - [MerchantPurchase](#create-a-merchantpurchase): Allows a merchant to charge their customers using debit or credit cards
+    - [MerchantInstallment](#query-merchantinstallments): Tracks the lifecycle of purchase installments
     - [Split](#query-splits): Split received Invoice payments between different receivers
     - [SplitReceiver](#create-splitreceivers): Receiver of an Invoice split
     - [Webhooks](#create-a-webhook-subscription): Configure your webhook endpoints and subscriptions
@@ -2333,6 +2337,210 @@ log = starkbank.splitreceiver.log.get("5155165527080960")
 print(log)
 ```
 
+## Query MerchantCards
+
+Get a list of merchant cards in chunks of at most 100. If you need smaller chunks, use the limit parameter.
+
+```python
+import starkbank
+
+merchant_cards = starkbank.merchantcard.query(limit=3)
+for merchant_card in merchant_cards:
+    print(merchant_card)
+```
+
+## Get a MerchantCard
+
+Retrieve detailed information about a specific card by its id.
+
+```python
+import starkbank
+
+merchantcard = starkbank.merchantcard.get('5950134772826112')
+print(merchantcard)
+```
+
+## Create a MerchantSession
+
+The Merchant Session allows you to create a session prior to a purchase.
+Sessions are essential for defining the parameters of a purchase, including funding type, expiration, 3DS, and more.
+
+```python
+import starkbank
+
+merchant_session = starkbank.merchantsession.create({
+    "allowedFundingTypes": [
+        "debit",
+        "credit"
+    ],
+    "allowedInstallments": [
+        {
+            "totalAmount": 0,
+            "count": 1
+        },
+        {
+            "totalAmount": 12000,
+            "count": 2
+        },
+        {
+            "totalAmount": 18000,
+            "count": 12
+        }
+    ],
+    "expiration": 3600,
+    "challengeMode": "disabled",
+    "tags": [
+        "your-tags"
+    ]
+})
+
+print(merchant_session)
+```
+
+You can create a MerchantPurchase through a MerchantSession by passing its UUID.
+**Note**: This method must be implemented in your front-end to ensure that sensitive card data does not pass through the back-end of the integration.
+
+## Create a MerchantSession Purchase
+
+This route can be used to create a Merchant Purchase directly from the payer's client application.
+The UUID of a Merchant Session that was previously created by the merchant is necessary to access this route.
+
+```python
+import starkbank
+
+merchant_session_purchase = starkbank.merchantsession.purchase(
+      uuid="0bb894a2697d41d99fe02cad2c00c9bc",
+      amount=18000,
+      installment_count=12,
+      card_expiration="2035-01",
+      card_number="5448280000000007",
+      card_security_code="123",
+      holder_name="Margaery Tyrell",
+      holder_email="margaery.tyrell@email.com",
+      holder_phone="11998663456",
+      funding_type="credit",
+      billing_country_code="BRA",
+      billing_city="São Paulo",
+      billing_state_code="SP",
+      billing_street_line1="Rua do Jardim de cima, 123",
+      billing_street_line2="1 andar",
+      billing_zip_code="11111-111",
+      metadata={
+        "extraData": "extraData",
+        "language": "pt-BR",
+        "timezoneOffset": 3,
+        "userAgent": "Mozilla",
+        "userIp": "255.255.255.255"
+      }
+    )
+
+print(merchant_session_purchase)
+```
+
+## Query MerchantSessions
+
+Get a list of merchant sessions in chunks of at most 100. If you need smaller chunks, use the limit parameter.
+
+```python
+import starkbank
+
+merchant_sessions = starkbank.merchantsession.query(limit=3)
+for merchant_session in merchant_sessions:
+    print(merchant_session)
+```
+
+## Get a MerchantSession
+
+Retrieve detailed information about a specific session by its id.
+
+```python
+import starkbank
+
+merchant_session = starkbank.merchantsession.get('5950134772826112')
+print(merchant_session)
+```
+
+## Create a MerchantPurchase
+
+The Merchant Purchase resource can be used to charge customers with credit or debit cards.
+If a card hasn't been used before, a Merchant Session Purchase must be created and approved with that specific card before it can be used directly in a Merchant Purchase.
+
+```python
+import starkbank
+
+merchant_purchase = starkbank.merchantpurchase.create(
+    starkbank.MerchantPurchase(
+        amount=10000,
+        installment_count=5,
+        card_id="6295415968235520",
+        funding_type="credit",
+        challenge_mode="disabled",
+        billing_city="Sao Paulo",
+        billing_country_code="BRA",
+        billing_state_code="SP",
+        billing_street_line_1="Rua Casterly Rock, 2000",
+        billing_street_line_2="1 andar",
+        billing_zip_code="11111-111",
+        holder_email="tywin.lannister@gmail.com",
+        holder_phone="11985923451",
+        metadata={
+            "userAgent": "userAgent",
+            "userIp": "255.255.255.255",
+            "language": "pt-BR",
+            "timezoneOffset": 3,
+            "extraData": "extraData"
+        },
+        tags=["teste"]
+    )
+)
+```
+
+## Query MerchantPurchases
+
+Get a list of merchant purchases in chunks of at most 100. If you need smaller chunks, use the limit parameter.
+
+```python
+import starkbank
+
+merchant_purchases = starkbank.merchantpurchase.query(limit=3)
+for merchant_purchase in merchant_purchases:
+    print(merchant_purchase)
+```
+
+## Get a MerchantPurchase
+
+Retrieve detailed information about a specific purchase by its id.
+
+```python
+import starkbank
+
+merchant_purchase = starkbank.merchantpurchase.get('5950134772826112')
+print(merchant_purchase)
+```
+
+## Query MerchantInstallments
+
+Get a list of merchant installments in chunks of at most 100. If you need smaller chunks, use the limit parameter.
+
+```python
+import starkbank
+
+merchant_installments = starkbank.merchantinstallment.query(limit=3)
+for merchant_installment in merchant_installments:
+    print(merchant_installment)
+```
+
+## Get a MerchantInstallment
+
+Retrieve detailed information about a specific installment by its id.
+
+```python
+import starkbank
+
+merchant_installment = starkbank.merchantinstallment.get('5950134772826112')
+print(merchant_installment)
+```
+
 ## Create a webhook subscription
 
 To create a webhook subscription and be notified whenever an event occurs, run:
diff --git a/starkbank/__init__.py b/starkbank/__init__.py
index 8aa04ce3..638f68f0 100644
--- a/starkbank/__init__.py
+++ b/starkbank/__init__.py
@@ -58,12 +58,24 @@
 from . import corporaterule
 from .corporaterule.__corporaterule import CorporateRule
 
+from . import merchantcard
+from .merchantcard.__merchantcard import MerchantCard
+
 from . import merchantcategory
 from .merchantcategory.__merchantcategory import MerchantCategory
 
 from . import merchantcountry
 from .merchantcountry.__merchantcountry import MerchantCountry
 
+from . import merchantinstallment
+from .merchantinstallment.__merchantinstallment import MerchantInstallment
+
+from . import merchantpurchase
+from .merchantpurchase.__merchantpurchase import MerchantPurchase
+
+from . import merchantsession
+from .merchantsession.__merchantsession import MerchantSession
+
 from . import cardmethod
 from .cardmethod.__cardmethod import CardMethod
 
diff --git a/starkbank/merchantcard/__init__.py b/starkbank/merchantcard/__init__.py
new file mode 100644
index 00000000..a48b0f12
--- /dev/null
+++ b/starkbank/merchantcard/__init__.py
@@ -0,0 +1,3 @@
+from .__merchantcard import get, query, page
+from .log.__log import Log
+from . import log
diff --git a/starkbank/merchantcard/__merchantcard.py b/starkbank/merchantcard/__merchantcard.py
new file mode 100644
index 00000000..f4b5e23a
--- /dev/null
+++ b/starkbank/merchantcard/__merchantcard.py
@@ -0,0 +1,55 @@
+from ..utils import rest
+from starkcore.utils.resource import Resource
+from starkcore.utils.checks import check_date, check_datetime, check_datetime_or_date
+
+class MerchantCard(Resource):
+    """# MerchantCard object
+    Check out our API Documentation at https://starkbank.com/docs/api#merchant-card
+    """
+
+    def __init__(self, id=None, ending=None , funding_type=None, holder_name=None, network=None, status=None, tags=None,
+                 expiration=None, created=None, updated=None):
+        Resource.__init__(self, id=id)
+        self.ending = ending
+        self.funding_type = funding_type
+        self.holder_name = holder_name
+        self.network = network
+        self.status = status
+        self.tags = tags
+        self.expiration = check_datetime_or_date(expiration)
+        self.created = check_datetime(created)
+        self.updated = check_datetime(updated)
+
+
+_resource = {"class": MerchantCard, "name": "MerchantCard"}
+
+
+def get(id, user=None):
+    return rest.get_id(resource=_resource, id=id, user=user)
+
+
+def query(limit=None, after=None, before=None, status=None, tags=None, ids=None, user=None):
+    return rest.get_stream(
+        resource=_resource,
+        limit=limit,
+        after=check_date(after),
+        before=check_date(before),
+        status=status,
+        tags=tags,
+        ids=ids,
+        user=user,
+    )
+
+
+def page(cursor=None, limit=None, after=None, before=None, status=None, tags=None, ids=None, user=None):
+    return rest.get_page(
+        resource=_resource,
+        cursor=cursor,
+        limit=limit,
+        after=check_date(after),
+        before=check_date(before),
+        status=status,
+        tags=tags,
+        ids=ids,
+        user=user,
+    )
diff --git a/starkbank/merchantcard/log/__init__.py b/starkbank/merchantcard/log/__init__.py
new file mode 100644
index 00000000..a8a69bac
--- /dev/null
+++ b/starkbank/merchantcard/log/__init__.py
@@ -0,0 +1 @@
+from .__log import query, page, get
diff --git a/starkbank/merchantcard/log/__log.py b/starkbank/merchantcard/log/__log.py
new file mode 100644
index 00000000..a0993c7b
--- /dev/null
+++ b/starkbank/merchantcard/log/__log.py
@@ -0,0 +1,53 @@
+from ...utils import rest
+from starkcore.utils.resource import Resource
+from starkcore.utils.api import from_api_json
+from starkcore.utils.checks import check_date, check_datetime
+from ..__merchantcard import _resource as _merchant_card_resource
+
+
+class Log(Resource):
+    """# merchantcard.Log object
+    Check out our API Documentation at https://starkbank.com/docs/api#merchant-card
+    """
+
+    def __init__(self, id, created, updated, type, errors, card):
+        Resource.__init__(self, id=id)
+        self.created = check_datetime(created)
+        self.updated = check_datetime(updated)
+        self.type = type
+        self.errors = errors
+        self.card = from_api_json(_merchant_card_resource, card)
+
+
+_resource = {"class": Log, "name": "MerchantCardLog"}
+
+
+def get(id, user=None):
+    return rest.get_id(resource=_resource, id=id, user=user)
+
+
+def query(limit=None, status=None, tags=None, ids=None, after=None, before=None, user=None):
+    return rest.get_stream(
+        resource=_resource,
+        limit=limit,
+        after=check_date(after),
+        before=check_date(before),
+        status=status,
+        tags=tags,
+        ids=ids,
+        user=user,
+    )
+
+
+def page(cursor=None, limit=None, after=None, before=None, types=None, ids=None, user=None):
+    return rest.get_page(
+        resource=_resource,
+        cursor=cursor,
+        limit=limit,
+        after=check_date(after),
+        before=check_date(before),
+        types=types,
+        ids=ids,
+        user=user,
+    )
+
diff --git a/starkbank/merchantinstallment/__init__.py b/starkbank/merchantinstallment/__init__.py
new file mode 100644
index 00000000..66f75d91
--- /dev/null
+++ b/starkbank/merchantinstallment/__init__.py
@@ -0,0 +1,3 @@
+from .__merchantinstallment import get, query, page
+from .log.__log import Log
+from . import log
diff --git a/starkbank/merchantinstallment/__merchantinstallment.py b/starkbank/merchantinstallment/__merchantinstallment.py
new file mode 100644
index 00000000..83c2bf66
--- /dev/null
+++ b/starkbank/merchantinstallment/__merchantinstallment.py
@@ -0,0 +1,60 @@
+from ..utils import rest
+from starkcore.utils.resource import Resource
+from starkcore.utils.checks import check_date, check_datetime, check_datetime_or_date
+
+
+class MerchantInstallment(Resource):
+    """# MerchantInstallment object
+    Check out our API Documentation at https://starkbank.com/docs/api#merchant-installment
+    """
+
+    def __init__(self, id=None, amount=None, created=None, due=None, fee=None, funding_type=None, network=None,
+                 purchase_id=None, status=None, tags=None, transaction_ids=None, updated=None):
+        Resource.__init__(self, id=id)
+
+        self.amount = amount
+        self.due = check_datetime_or_date(due)
+        self.fee = fee
+        self.funding_type = funding_type
+        self.network = network
+        self.purchase_id = purchase_id
+        self.status = status
+        self.tags = tags
+        self.transaction_ids = transaction_ids
+        self.created = check_datetime(created)
+        self.updated = check_datetime(updated)
+
+
+_resource = {"class": MerchantInstallment, "name": "MerchantInstallment"}
+
+
+def get(id, user=None):
+    return rest.get_id(resource=_resource, id=id, user=user)
+
+
+def query(limit=None, after=None, before=None, status=None, tags=None, ids=None, user=None):
+    return rest.get_stream(
+        resource=_resource,
+        limit=limit,
+        after=check_date(after),
+        before=check_date(before),
+        status=status,
+        tags=tags,
+        ids=ids,
+        user=user,
+    )
+
+
+def page(cursor=None, limit=None, after=None, before=None, status=None, tags=None, ids=None, user=None):
+    return rest.get_page(
+        resource=_resource,
+        cursor=cursor,
+        limit=limit,
+        after=check_date(after),
+        before=check_date(before),
+        status=status,
+        tags=tags,
+        ids=ids,
+        user=user,
+    )
+
diff --git a/starkbank/merchantinstallment/log/__init__.py b/starkbank/merchantinstallment/log/__init__.py
new file mode 100644
index 00000000..a8a69bac
--- /dev/null
+++ b/starkbank/merchantinstallment/log/__init__.py
@@ -0,0 +1 @@
+from .__log import query, page, get
diff --git a/starkbank/merchantinstallment/log/__log.py b/starkbank/merchantinstallment/log/__log.py
new file mode 100644
index 00000000..b1fbfcc9
--- /dev/null
+++ b/starkbank/merchantinstallment/log/__log.py
@@ -0,0 +1,53 @@
+from ...utils import rest
+from starkcore.utils.resource import Resource
+from starkcore.utils.api import from_api_json
+from starkcore.utils.checks import check_date, check_datetime
+from ..__merchantinstallment import _resource as _merchant_installment_resource
+
+
+class Log(Resource):
+    """# merchantinstallment.Log object
+    Check out our API Documentation at https://starkbank.com/docs/api#merchant-installment
+    """
+
+    def __init__(self, id, created, updated, type, errors, installment):
+        Resource.__init__(self, id=id)
+        self.created = check_datetime(created)
+        self.updated = check_datetime(updated)
+        self.type = type
+        self.errors = errors
+        self.installment = from_api_json(_merchant_installment_resource, installment)
+
+
+_resource = {"class": Log, "name": "MerchantInstallmentLog"}
+
+
+def get(id, user=None):
+    return rest.get_id(resource=_resource, id=id, user=user)
+
+
+def query(limit=None, status=None, tags=None, ids=None, after=None, before=None, user=None):
+    return rest.get_stream(
+        resource=_resource,
+        limit=limit,
+        after=check_date(after),
+        before=check_date(before),
+        status=status,
+        tags=tags,
+        ids=ids,
+        user=user,
+    )
+
+
+def page(cursor=None, limit=None, after=None, before=None, types=None, ids=None, user=None):
+    return rest.get_page(
+        resource=_resource,
+        cursor=cursor,
+        limit=limit,
+        after=check_date(after),
+        before=check_date(before),
+        types=types,
+        ids=ids,
+        user=user,
+    )
+
diff --git a/starkbank/merchantpurchase/__init__.py b/starkbank/merchantpurchase/__init__.py
new file mode 100644
index 00000000..bb9e9e82
--- /dev/null
+++ b/starkbank/merchantpurchase/__init__.py
@@ -0,0 +1,3 @@
+from .__merchantpurchase import get, query, page, create, update
+from .log.__log import Log
+from . import log
diff --git a/starkbank/merchantpurchase/__merchantpurchase.py b/starkbank/merchantpurchase/__merchantpurchase.py
new file mode 100644
index 00000000..25965a79
--- /dev/null
+++ b/starkbank/merchantpurchase/__merchantpurchase.py
@@ -0,0 +1,94 @@
+from starkcore.utils.resource import Resource
+from ..utils import rest
+from starkcore.utils.checks import check_date, check_datetime
+
+
+class MerchantPurchase(Resource):
+    """# MerchantPurchase object
+    Check out our API Documentation at https://starkbank.com/docs/api#merchant-purchase
+    """
+
+    def __init__(self, amount, card_id, funding_type, installment_count, id=None, card_expiration=None,
+                 card_number=None, card_security_code=None,holder_name=None, holder_email=None, holder_phone=None,
+                 billing_country_code=None, billing_city=None,billing_state_code=None, billing_street_line_1=None,
+                 billing_street_line_2=None, billing_zip_code=None, metadata=None, card_ending=None,
+                 challenge_mode=None, challenge_url=None, created=None, currency_code=None, end_to_end_id=None,
+                 fee=None, network=None, source=None, status=None, tags=None, updated=None):
+        Resource.__init__(self, id=id)
+
+        self.amount = amount
+        self.installment_count = installment_count
+        self.card_expiration = card_expiration
+        self.card_number = card_number
+        self.card_security_code = card_security_code
+        self.holder_name = holder_name
+        self.holder_email = holder_email
+        self.holder_phone = holder_phone
+        self.funding_type = funding_type
+        self.billing_country_code = billing_country_code
+        self.billing_city = billing_city
+        self.billing_state_code = billing_state_code
+        self.billing_street_line_1 = billing_street_line_1
+        self.billing_street_line_2 = billing_street_line_2
+        self.billing_zip_code = billing_zip_code
+        self.metadata = metadata
+        self.card_ending = card_ending
+        self.card_id = card_id
+        self.challenge_mode = challenge_mode
+        self.challenge_url = challenge_url
+        self.currency_code = currency_code
+        self.end_to_end_id = end_to_end_id
+        self.fee = fee
+        self.network = network
+        self.source = source
+        self.status = status
+        self.tags = tags
+        self.created = check_datetime(created)
+        self.updated = check_datetime(updated)
+
+
+_resource = {"class": MerchantPurchase, "name": "MerchantPurchase"}
+
+
+def create(merchant_purchase, user=None):
+    return rest.post_single(resource=_resource, entity=merchant_purchase, user=user)
+
+
+def get(id, user=None):
+    return rest.get_id(resource=_resource, id=id, user=user)
+
+
+def query(limit=None, after=None, before=None, status=None, tags=None, ids=None, user=None):
+    return rest.get_stream(
+        resource=_resource,
+        limit=limit,
+        after=check_date(after),
+        before=check_date(before),
+        status=status,
+        tags=tags,
+        ids=ids,
+        user=user,
+    )
+
+
+def page(cursor=None, limit=None, after=None, before=None, status=None, tags=None, ids=None, user=None):
+    return rest.get_page(
+        resource=_resource,
+        cursor=cursor,
+        limit=limit,
+        after=check_date(after),
+        before=check_date(before),
+        status=status,
+        tags=tags,
+        ids=ids,
+        user=user,
+    )
+
+
+def update(id, status=None, amount=None, user=None):
+    payload = {
+        "status": status,
+        "amount": amount
+    }
+    return rest.patch_id(resource=_resource, id=id, user=user, payload=payload)
+
diff --git a/starkbank/merchantpurchase/log/__init__.py b/starkbank/merchantpurchase/log/__init__.py
new file mode 100644
index 00000000..a8a69bac
--- /dev/null
+++ b/starkbank/merchantpurchase/log/__init__.py
@@ -0,0 +1 @@
+from .__log import query, page, get
diff --git a/starkbank/merchantpurchase/log/__log.py b/starkbank/merchantpurchase/log/__log.py
new file mode 100644
index 00000000..b19bd90f
--- /dev/null
+++ b/starkbank/merchantpurchase/log/__log.py
@@ -0,0 +1,52 @@
+from ...utils import rest
+from starkcore.utils.resource import Resource
+from starkcore.utils.api import from_api_json
+from starkcore.utils.checks import check_date, check_datetime
+from ..__merchantpurchase import _resource as _merchant_purchase_resource
+
+
+class Log(Resource):
+    """# merchantpurchase.Log object
+    Check out our API Documentation at https://starkbank.com/docs/api#merchant-purchase
+    """
+
+    def __init__(self, id, created, type, errors, purchase):
+        Resource.__init__(self, id=id)
+
+        self.created = check_datetime(created)
+        self.type = type
+        self.errors = errors
+        self.purchase = from_api_json(_merchant_purchase_resource, purchase)
+
+
+_resource = {"class": Log, "name": "MerchantPurchaseLog"}
+
+
+def get(id, user=None):
+    return rest.get_id(resource=_resource, id=id, user=user)
+
+
+def query(limit=None, status=None, tags=None, ids=None, after=None, before=None, user=None):
+    return rest.get_stream(
+        resource=_resource,
+        limit=limit,
+        after=check_date(after),
+        before=check_date(before),
+        status=status,
+        tags=tags,
+        ids=ids,
+        user=user,
+    )
+
+def page(cursor=None, limit=None, after=None, before=None, types=None, ids=None, user=None):
+    return rest.get_page(
+        resource=_resource,
+        cursor=cursor,
+        limit=limit,
+        after=check_date(after),
+        before=check_date(before),
+        types=types,
+        ids=ids,
+        user=user,
+    )
+
diff --git a/starkbank/merchantsession/__init__.py b/starkbank/merchantsession/__init__.py
new file mode 100644
index 00000000..ad4c27bc
--- /dev/null
+++ b/starkbank/merchantsession/__init__.py
@@ -0,0 +1,5 @@
+from .__merchantsession import create, get, query, page, purchase
+from .log.__log import Log
+from . import log
+from .__purchase import Purchase
+from .allowedinstallment.__allowedinstallment import AllowedInstallment
diff --git a/starkbank/merchantsession/__merchantsession.py b/starkbank/merchantsession/__merchantsession.py
new file mode 100644
index 00000000..3ed48022
--- /dev/null
+++ b/starkbank/merchantsession/__merchantsession.py
@@ -0,0 +1,83 @@
+from starkcore.utils.resource import Resource
+from ..utils import rest
+from starkcore.utils.api import from_api_json
+from .allowedinstallment.__allowedinstallment import AllowedInstallment
+from .allowedinstallment.__allowedinstallment import _sub_resource as _allowed_installments_sub_resource
+from starkcore.utils.checks import check_date, check_datetime
+from .__purchase import _resource as purchase_resource
+
+
+class MerchantSession(Resource):
+    """# MerchantSession object
+    Check out our API Documentation at https://starkbank.com/docs/api#merchant-session
+    """
+
+    def __init__(self, allowed_funding_types, allowed_installments, expiration, id=None, allowed_ips=None,
+                 challenge_mode=None, created=None, status=None, tags=None, updated=None, uuid=None):
+        Resource.__init__(self, id=id)
+
+        self.allowed_funding_types = allowed_funding_types
+        self.allowed_installments = _parse_allowed_installments(allowed_installments)
+        self.allowed_ips = allowed_ips
+        self.challenge_mode = challenge_mode
+        self.expiration = expiration
+        self.status = status
+        self.tags = tags
+        self.created = check_datetime(created)
+        self.updated = check_datetime(updated)
+        self.uuid = uuid
+
+
+_resource = {"class": MerchantSession, "name": "MerchantSession"}
+
+
+def _parse_allowed_installments(allowed_installments):
+    if allowed_installments is None:
+        return []
+    parsed_allowed_installments = []
+    for allowed_installment in allowed_installments:
+        if isinstance(allowed_installment, AllowedInstallment):
+            parsed_allowed_installments.append(allowed_installment)
+            continue
+        parsed_allowed_installments.append(from_api_json(_allowed_installments_sub_resource, allowed_installment))
+    return parsed_allowed_installments
+
+
+def create(merchant_session, user=None):
+    return rest.post_single(resource=_resource, entity=merchant_session, user=user)
+
+
+def get(id, user=None):
+    return rest.get_id(resource=_resource, id=id, user=user)
+
+
+def query(limit=None, status=None, tags=None, ids=None, after=None, before=None, user=None):
+    return rest.get_stream(
+        resource=_resource,
+        limit=limit,
+        after=check_date(after),
+        before=check_date(before),
+        status=status,
+        tags=tags,
+        ids=ids,
+        user=user,
+    )
+
+
+def page(cursor=None, limit=None, status=None, tags=None, ids=None, after=None, before=None, user=None):
+    return rest.get_page(
+        resource=_resource,
+        cursor=cursor,
+        limit=limit,
+        after=check_date(after),
+        before=check_date(before),
+        status=status,
+        tags=tags,
+        ids=ids,
+        user=user,
+    )
+
+
+def purchase(uuid, purchase, user=None):
+    return rest.post_sub_resource(resource=_resource, id=uuid, sub_resource=purchase_resource, entity=purchase, user=user)
+
diff --git a/starkbank/merchantsession/__purchase.py b/starkbank/merchantsession/__purchase.py
new file mode 100644
index 00000000..adabd13e
--- /dev/null
+++ b/starkbank/merchantsession/__purchase.py
@@ -0,0 +1,50 @@
+from starkcore.utils.resource import Resource
+from starkcore.utils.checks import check_datetime
+
+
+class Purchase(Resource):
+    """# Purchase object
+     Check out our API Documentation at https://starkbank.com/docs/api#merchant-session
+    """
+
+    def __init__(self, amount, card_expiration, card_number, card_security_code, holder_name, funding_type, id=None,
+                 holder_email=None, holder_phone=None, installment_count=None, billing_country_code=None,
+                 billing_city=None, billing_state_code=None, billing_street_line_1=None, billing_street_line_2=None,
+                 billing_zip_code=None, metadata=None, card_ending=None, card_id=None, challenge_mode=None,
+                 challenge_url=None, created=None, currency_code=None, end_to_end_id=None, fee=None, network=None,
+                 source=None, status=None, tags=None, updated=None):
+        Resource.__init__(self, id=id)
+
+        self.amount = amount
+        self.card_expiration = card_expiration
+        self.card_number = card_number
+        self.card_security_code = card_security_code
+        self.holder_name = holder_name
+        self.funding_type = funding_type
+        self.holder_email = holder_email
+        self.holder_phone = holder_phone
+        self.installment_count = installment_count
+        self.billing_country_code = billing_country_code
+        self.billing_city = billing_city
+        self.billing_state_code = billing_state_code
+        self.billing_street_line_1 = billing_street_line_1
+        self.billing_street_line_2 = billing_street_line_2
+        self.billing_zip_code = billing_zip_code
+        self.metadata = metadata
+        self.card_ending = card_ending
+        self.card_id = card_id
+        self.challenge_mode = challenge_mode
+        self.challenge_url = challenge_url
+        self.currency_code = currency_code
+        self.end_to_end_id = end_to_end_id
+        self.fee = fee
+        self.network = network
+        self.source = source
+        self.status = status
+        self.tags = tags
+        self.created = check_datetime(created)
+        self.updated = check_datetime(updated)
+
+
+_resource = {"class": Purchase, "name": "Purchase"}
+
diff --git a/starkbank/merchantsession/allowedinstallment/__allowedinstallment.py b/starkbank/merchantsession/allowedinstallment/__allowedinstallment.py
new file mode 100644
index 00000000..404050e8
--- /dev/null
+++ b/starkbank/merchantsession/allowedinstallment/__allowedinstallment.py
@@ -0,0 +1,12 @@
+from starkcore.utils.subresource import SubResource
+
+
+class AllowedInstallment(SubResource):
+    def __init__(self, total_amount, count):
+        super().__init__()
+        self.total_amount = total_amount
+        self.count = count
+
+
+_sub_resource = {"class": AllowedInstallment, "name": "AllowedInstallment"}
+
diff --git a/starkbank/merchantsession/allowedinstallment/__init__.py b/starkbank/merchantsession/allowedinstallment/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/starkbank/merchantsession/log/__init__.py b/starkbank/merchantsession/log/__init__.py
new file mode 100644
index 00000000..a8a69bac
--- /dev/null
+++ b/starkbank/merchantsession/log/__init__.py
@@ -0,0 +1 @@
+from .__log import query, page, get
diff --git a/starkbank/merchantsession/log/__log.py b/starkbank/merchantsession/log/__log.py
new file mode 100644
index 00000000..3a7ee030
--- /dev/null
+++ b/starkbank/merchantsession/log/__log.py
@@ -0,0 +1,52 @@
+from starkcore.utils.resource import Resource
+from ...utils import rest
+from starkcore.utils.api import from_api_json
+from starkcore.utils.checks import check_date, check_datetime
+from ..__merchantsession import _resource as _merchant_session_resource
+
+
+class Log(Resource):
+    """# merchantsession.Log object
+    Check out our API Documentation at https://starkbank.com/docs/api#merchant-session
+    """
+
+    def __init__(self, id, created, type, errors, session):
+        Resource.__init__(self, id=id)
+        self.created = check_datetime(created)
+        self.type = type
+        self.errors = errors
+        self.session = from_api_json(_merchant_session_resource, session)
+
+
+_resource = {"class": Log, "name": "MerchantSessionLog"}
+
+
+def get(id, user=None):
+    return rest.get_id(resource=_resource, id=id, user=user)
+
+
+def query(limit=None, status=None, tags=None, ids=None, after=None, before=None, user=None):
+    return rest.get_stream(
+        resource=_resource,
+        limit=limit,
+        after=check_date(after),
+        before=check_date(before),
+        status=status,
+        tags=tags,
+        ids=ids,
+        user=user,
+    )
+
+
+def page(cursor=None, limit=None, after=None, before=None, types=None, ids=None, user=None):
+    return rest.get_page(
+        resource=_resource,
+        cursor=cursor,
+        limit=limit,
+        after=check_date(after),
+        before=check_date(before),
+        types=types,
+        ids=ids,
+        user=user,
+    )
+
diff --git a/starkbank/utils/rest.py b/starkbank/utils/rest.py
index 4b4a413f..83987818 100644
--- a/starkbank/utils/rest.py
+++ b/starkbank/utils/rest.py
@@ -8,6 +8,7 @@
 get_content = set_relay(rest.get_content)
 get_sub_resource = set_relay(rest.get_sub_resource)
 get_sub_resources = set_relay(rest.get_sub_resources)
+post_sub_resource = set_relay(rest.post_sub_resource)
 post_multi = set_relay(rest.post_multi)
 post_single = set_relay(rest.post_single)
 delete_id = set_relay(rest.delete_id)
diff --git a/tests/sdk/test_corporate_card_log.py b/tests/sdk/test_corporate_card_log.py
index bb3e45bb..cc00ef4a 100644
--- a/tests/sdk/test_corporate_card_log.py
+++ b/tests/sdk/test_corporate_card_log.py
@@ -2,6 +2,7 @@
 from unittest import TestCase, main
 from tests.utils.user import exampleProject
 
+
 starkbank.user = exampleProject
 
 
@@ -16,7 +17,7 @@ def test_success(self):
 class TestCorporateCardLogGet(TestCase):
 
     def test_success(self):
-        logs = starkbank.corporatecard.log.query(limit=1)
+        logs = starkbank.corporatecard.log.query(limit=1, types=["created"])
         log = starkbank.corporatecard.log.get(id=next(logs).id)
         self.assertEqual(log.id, str(log.id))
 
diff --git a/tests/sdk/test_merchant_card.py b/tests/sdk/test_merchant_card.py
new file mode 100644
index 00000000..5fbca193
--- /dev/null
+++ b/tests/sdk/test_merchant_card.py
@@ -0,0 +1,41 @@
+import starkbank
+from unittest import TestCase, main
+from tests.utils.user import exampleProject
+
+starkbank.user = exampleProject
+
+class TestMerchantCardQuery(TestCase):
+
+    def test_success(self):
+        merchant_cards = starkbank.merchantcard.query(limit=3)
+        for merchant_card in merchant_cards:
+            self.assertIsInstance(merchant_card.id, str)
+
+
+class TestMerchantCardGet(TestCase):
+
+    def test_success(self):
+        merchant_cards = starkbank.merchantcard.query(limit=3)
+        for purchase in merchant_cards:
+            merchant_card = starkbank.merchantcard.get(purchase.id)
+            self.assertIsInstance(merchant_card.id, str)
+
+
+class TestMerchantCardPage(TestCase):
+
+    def test_success(self):
+        ids = []
+        cursor = None
+        for _ in range(2):
+            page, cursor = starkbank.merchantcard.page(limit=5, cursor=cursor)
+            for entity in page:
+                self.assertNotIn(entity.id, ids)
+                ids.append(entity.id)
+            if cursor is None:
+                break
+        self.assertEqual(len(ids), 10)
+
+
+if __name__ == '__main__':
+    main()
+
diff --git a/tests/sdk/test_merchant_card_log.py b/tests/sdk/test_merchant_card_log.py
new file mode 100644
index 00000000..371e706f
--- /dev/null
+++ b/tests/sdk/test_merchant_card_log.py
@@ -0,0 +1,45 @@
+import starkbank
+from unittest import TestCase, main
+from tests.utils.user import exampleProject
+
+
+starkbank.user = exampleProject
+
+
+class TestMerchantCardQueryLog(TestCase):
+
+    def test_success(self):
+        merchant_card_logs = starkbank.merchantcard.log.query(limit=3)
+        for log in merchant_card_logs:
+            print(log)
+            self.assertIsInstance(log.id, str)
+
+
+class TestMerchantCardGetLog(TestCase):
+
+    def test_success(self):
+        merchant_card_logs = starkbank.merchantcard.log.query(limit=3)
+        for log in merchant_card_logs:
+            print(log.id)
+            log = starkbank.merchantcard.log.get(log.id)
+            self.assertIsInstance(log.id, str)
+
+
+class TestMerchantCardPageLog(TestCase):
+
+    def test_success(self):
+        ids = []
+        cursor = None
+        for _ in range(2):
+            page, cursor = starkbank.merchantcard.log.page(limit=5, cursor=cursor)
+            for entity in page:
+                self.assertNotIn(entity.id, ids)
+                ids.append(entity.id)
+            if cursor is None:
+                break
+        self.assertEqual(len(ids), 10)
+
+
+if __name__ == '__main__':
+    main()
+
diff --git a/tests/sdk/test_merchant_installment.py b/tests/sdk/test_merchant_installment.py
new file mode 100644
index 00000000..41683b53
--- /dev/null
+++ b/tests/sdk/test_merchant_installment.py
@@ -0,0 +1,43 @@
+import starkbank
+from unittest import TestCase, main
+from tests.utils.user import exampleProject
+
+
+starkbank.user = exampleProject
+
+
+class TestMerchantInstallmentQuery(TestCase):
+
+    def test_success(self):
+        merchant_installments = starkbank.merchantinstallment.query(limit=3)
+        for merchant_installment in merchant_installments:
+            self.assertIsInstance(merchant_installment.id, str)
+
+
+class TestMerchantInstallmentGet(TestCase):
+
+    def test_success(self):
+        merchant_installments = starkbank.merchantpurchase.query(limit=3)
+        for merchant_installment in merchant_installments:
+            merchant_installment = starkbank.merchantpurchase.get(merchant_installment.id)
+            self.assertIsInstance(merchant_installment.id, str)
+
+
+class TestMerchantInstallmentPage(TestCase):
+
+    def test_success(self):
+        ids = []
+        cursor = None
+        for _ in range(2):
+            page, cursor = starkbank.merchantinstallment.page(limit=5, cursor=cursor)
+            for entity in page:
+                self.assertNotIn(entity.id, ids)
+                ids.append(entity.id)
+            if cursor is None:
+                break
+        self.assertEqual(len(ids), 10)
+
+
+if __name__ == '__main__':
+    main()
+
diff --git a/tests/sdk/test_merchant_installment_log.py b/tests/sdk/test_merchant_installment_log.py
new file mode 100644
index 00000000..fd6f2649
--- /dev/null
+++ b/tests/sdk/test_merchant_installment_log.py
@@ -0,0 +1,45 @@
+import starkbank
+from unittest import TestCase, main
+from tests.utils.user import exampleProject
+
+
+starkbank.user = exampleProject
+
+
+class TestMerchantInstallmentQueryLog(TestCase):
+
+    def test_success(self):
+        merchant_installment_logs = starkbank.merchantinstallment.log.query(limit=3)
+        for log in merchant_installment_logs:
+            print(log)
+            self.assertIsInstance(log.id, str)
+
+
+class TestMerchantInstallmentGetLog(TestCase):
+
+    def test_success(self):
+        merchant_installment_logs = starkbank.merchantinstallment.log.query(limit=3)
+        for log in merchant_installment_logs:
+            print(log.id)
+            log = starkbank.merchantinstallment.log.get(log.id)
+            self.assertIsInstance(log.id, str)
+
+
+class TestMerchantInstallmentPageLog(TestCase):
+
+    def test_success(self):
+        ids = []
+        cursor = None
+        for _ in range(2):
+            page, cursor = starkbank.merchantinstallment.log.page(limit=5, cursor=cursor)
+            for entity in page:
+                self.assertNotIn(entity.id, ids)
+                ids.append(entity.id)
+            if cursor is None:
+                break
+        self.assertEqual(len(ids), 10)
+
+
+if __name__ == '__main__':
+    main()
+
diff --git a/tests/sdk/test_merchant_purchase.py b/tests/sdk/test_merchant_purchase.py
new file mode 100644
index 00000000..6c2245ed
--- /dev/null
+++ b/tests/sdk/test_merchant_purchase.py
@@ -0,0 +1,66 @@
+import starkbank
+from unittest import TestCase, main
+from tests.utils.user import exampleProject
+from tests.utils.merchantPurchase import generate_example_merchant_purchase_json
+
+
+starkbank.user = exampleProject
+
+
+class TestMerchantPurchaseCreate(TestCase):
+
+    def test_success(self):
+        merchant_purchases = starkbank.merchantpurchase.query(status="confirmed", limit=1)
+        for merchant_purchase in merchant_purchases:
+            merchant_purchase_json = generate_example_merchant_purchase_json(merchant_purchase.card_id)
+            merchant_purchase_created = starkbank.merchantpurchase.create(merchant_purchase_json)
+            self.assertIsNotNone(merchant_purchase_created.id)
+
+
+class TestMerchantPurchaseQuery(TestCase):
+
+    def test_success(self):
+        merchant_purchases = starkbank.merchantpurchase.query(limit=3)
+        for merchant_purchase in merchant_purchases:
+            self.assertIsInstance(merchant_purchase.id, str)
+
+
+class TestMerchantPurchaseGet(TestCase):
+
+    def test_success(self):
+        merchant_purchases = starkbank.merchantpurchase.query(limit=3)
+        for purchase in merchant_purchases:
+            merchant_purchase = starkbank.merchantpurchase.get(purchase.id)
+            self.assertIsInstance(merchant_purchase.id, str)
+
+
+class TestMerchantPurchasePage(TestCase):
+
+    def test_success(self):
+        ids = []
+        cursor = None
+        for _ in range(2):
+            page, cursor = starkbank.merchantpurchase.page(limit=5, cursor=cursor)
+            for entity in page:
+                self.assertNotIn(entity.id, ids)
+                ids.append(entity.id)
+            if cursor is None:
+                break
+        self.assertEqual(len(ids), 10)
+
+
+class TestMerchantPurchaseUpdate(TestCase):
+
+    def test_success(self):
+        merchant_purchases = starkbank.merchantpurchase.query(limit=1, status="confirmed")
+        for merchant_purchase in merchant_purchases:
+            if merchant_purchase.amount == 0:
+                continue
+
+            merchant_purchase = starkbank.merchantpurchase.update(id=merchant_purchase.id, status="reversed", amount=0)
+            self.assertIsInstance(merchant_purchase.id, str)
+
+
+if __name__ == '__main__':
+    main()
+
diff --git a/tests/sdk/test_merchant_purchase_log.py b/tests/sdk/test_merchant_purchase_log.py
new file mode 100644
index 00000000..abed790b
--- /dev/null
+++ b/tests/sdk/test_merchant_purchase_log.py
@@ -0,0 +1,45 @@
+import starkbank
+from unittest import TestCase, main
+from tests.utils.user import exampleProject
+
+
+starkbank.user = exampleProject
+
+
+class TestMerchantPurchaseQueryLog(TestCase):
+
+    def test_success(self):
+        merchant_purchase_logs = starkbank.merchantpurchase.log.query(limit=3)
+        for log in merchant_purchase_logs:
+            print(log)
+            self.assertIsInstance(log.id, str)
+
+
+class TestMerchantPurchaseGetLog(TestCase):
+
+    def test_success(self):
+        merchant_purchase_logs = starkbank.merchantpurchase.log.query(limit=3)
+        for log in merchant_purchase_logs:
+            print(log.id)
+            log = starkbank.merchantpurchase.log.get(log.id)
+            self.assertIsInstance(log.id, str)
+
+
+class TestMerchantPurchasePageLog(TestCase):
+
+    def test_success(self):
+        ids = []
+        cursor = None
+        for _ in range(2):
+            page, cursor = starkbank.merchantpurchase.log.page(limit=5, cursor=cursor)
+            for entity in page:
+                self.assertNotIn(entity.id, ids)
+                ids.append(entity.id)
+            if cursor is None:
+                break
+        self.assertEqual(len(ids), 10)
+
+
+if __name__ == '__main__':
+    main()
+
diff --git a/tests/sdk/test_merchant_session.py b/tests/sdk/test_merchant_session.py
new file mode 100644
index 00000000..87cc3efb
--- /dev/null
+++ b/tests/sdk/test_merchant_session.py
@@ -0,0 +1,81 @@
+import starkbank
+from unittest import TestCase, main
+from tests.utils.user import exampleProject
+from tests.utils.merchantSession import generate_example_merchant_session_json, \
+    generate_example_merchant_session_purchase_challenge_mode_disabled_json, \
+    generate_example_merchant_session_purchase_challenge_mode_enabled_json
+
+
+starkbank.user = exampleProject
+
+
+class TestMerchantSessionCreate(TestCase):
+
+    def test_success(self):
+        merchant_session_json = generate_example_merchant_session_json("disabled")
+        merchant_session = starkbank.merchantsession.create(merchant_session_json)
+        self.assertIsNotNone(merchant_session.id)
+
+
+class TestMerchantSessionQuery(TestCase):
+
+    def test_success(self):
+        merchant_sessions = starkbank.merchantsession.query(limit=3)
+        for merchant_session in merchant_sessions:
+            self.assertIsInstance(merchant_session.id, str)
+
+
+class TestMerchantSessionGet(TestCase):
+
+    def test_success(self):
+        merchant_sessions = starkbank.merchantsession.query(limit=3)
+        for session in merchant_sessions:
+            merchant_session = starkbank.merchantsession.get(session.id)
+            self.assertIsInstance(merchant_session.id, str)
+
+
+class TestMerchantSessionPage(TestCase):
+
+    def test_success(self):
+        ids = []
+        cursor = None
+        for _ in range(2):
+            page, cursor = starkbank.merchantsession.page(limit=5, cursor=cursor)
+            for entity in page:
+                self.assertNotIn(entity.id, ids)
+                ids.append(entity.id)
+            if cursor is None:
+                break
+        self.assertEqual(len(ids), 10)
+
+
+class TestMerchantSessionPurchaseChallengeModeDisabled(TestCase):
+
+    def test_success(self):
+        merchant_session = starkbank.merchantsession.create(generate_example_merchant_session_json("disabled"))
+        merchant_session_purchase_json = generate_example_merchant_session_purchase_challenge_mode_disabled_json()
+        merchant_session_purchase = starkbank.merchantsession.purchase(
+            uuid=merchant_session.uuid,
+            purchase=merchant_session_purchase_json
+        )
+        self.assertIsNotNone(merchant_session_purchase.id)
+
+
+class TestMerchantSessionPurchaseChallengeModeEnabled(TestCase):
+
+    def test_success(self):
+        merchant_session_json = generate_example_merchant_session_json("enabled")
+        merchant_session = starkbank.merchantsession.create(merchant_session_json)
+
+        merchant_session_purchase_json = generate_example_merchant_session_purchase_challenge_mode_enabled_json()
+        purchase = starkbank.merchantsession.purchase(
+            uuid=merchant_session.uuid,
+            purchase=merchant_session_purchase_json
+        )
+
+        self.assertIsNotNone(purchase.id)
+
+
+if __name__ == '__main__':
+    main()
+
diff --git a/tests/sdk/test_merchant_session_log.py b/tests/sdk/test_merchant_session_log.py
new file mode 100644
index 00000000..ac0129b9
--- /dev/null
+++ b/tests/sdk/test_merchant_session_log.py
@@ -0,0 +1,44 @@
+import starkbank
+from unittest import TestCase, main
+from tests.utils.user import exampleProject
+
+
+starkbank.user = exampleProject
+
+
+class TestMerchantSessionQueryLog(TestCase):
+
+    def test_success(self):
+        merchant_session_logs = starkbank.merchantsession.log.query(limit=3)
+        for log in merchant_session_logs:
+            print(log)
+            self.assertIsInstance(log.id, str)
+
+
+class TestMerchantSessionGetLog(TestCase):
+
+    def test_success(self):
+        merchant_session_logs = starkbank.merchantsession.log.query(limit=3)
+        for log in merchant_session_logs:
+            log = starkbank.merchantsession.log.get(log.id)
+            self.assertIsInstance(log.id, str)
+
+
+class TestMerchantSessionPageLog(TestCase):
+
+    def test_success(self):
+        ids = []
+        cursor = None
+        for _ in range(2):
+            page, cursor = starkbank.merchantsession.log.page(limit=5, cursor=cursor)
+            for entity in page:
+                self.assertNotIn(entity.id, ids)
+                ids.append(entity.id)
+            if cursor is None:
+                break
+        self.assertEqual(len(ids), 10)
+
+
+if __name__ == '__main__':
+    main()
+
diff --git a/tests/utils/merchantPurchase.py b/tests/utils/merchantPurchase.py
new file mode 100644
index 00000000..7af6dfa7
--- /dev/null
+++ b/tests/utils/merchantPurchase.py
@@ -0,0 +1,93 @@
+# coding: utf-8
+import starkbank
+from copy import deepcopy
+from starkbank import MerchantPurchase
+
+
+def json_to_merchant_purchase(json_data):
+    return MerchantPurchase(
+        amount=json_data.get("amount"),
+        installment_count=json_data.get("installmentCount"),
+        card_expiration=json_data.get("cardExpiration"),
+        card_number=json_data.get("cardNumber"),
+        card_security_code=json_data.get("cardSecurityCode"),
+        holder_name=json_data.get("holderName"),
+        holder_email=json_data.get("holderEmail"),
+        holder_phone=json_data.get("holderPhone"),
+        funding_type=json_data.get("fundingType"),
+        billing_country_code=json_data.get("billingCountryCode"),
+        billing_city=json_data.get("billingCity"),
+        billing_state_code=json_data.get("billingStateCode"),
+        billing_street_line_1=json_data.get("billingStreetLine1"),
+        billing_street_line_2=json_data.get("billingStreetLine2"),
+        billing_zip_code=json_data.get("billingZipCode"),
+        metadata=json_data.get("metadata"),
+        card_id=json_data.get("cardId")
+    )
+
+
+json_data = {
+    "amount": 180,
+    "installmentCount": 12,
+    "cardExpiration": "2035-01",
+    "cardNumber": "5277696455399733",
+    "cardSecurityCode": "123",
+    "holderName": "Holder Name",
+    "holderEmail": "holdeName@email.com",
+    "holderPhone": "11111111111",
+    "fundingType": "credit",
+    "billingCountryCode": "BRA",
+    "billingCity": "São Paulo",
+    "billingStateCode": "SP",
+    "billingStreetLine1": "Rua do Holder Name, 123",
+    "billingStreetLine2": "",
+    "billingZipCode": "11111-111",
+    "metadata": {
+        "userAgent": "Postman",
+        "userIp": "255.255.255.255",
+        "language": "pt-BR",
+        "timezoneOffset": 3,
+        "extraData": "extraData"
+    }
+}
+
+merchant_purchase = json_to_merchant_purchase(json_data)
+
+
+def generate_example_merchant_purchase_json(card_id):
+    merchant_purchase_json = {
+            "amount": 10000,
+            "installmentCount": 5,
+            "cardId": card_id,
+            "fundingType": "credit",
+            "challengeMode": "disabled",
+            "billingCity": "Sao Paulo",
+            "billingCountryCode": "BRA",
+            "billingStateCode": "SP",
+            "billingStreetLine1": "Rua do Holder Name, 123",
+            "billingStreetLine2": "1 andar",
+            "billingZipCode": "11111-111",
+            "holderEmail": "holdeName@email.com",
+            "holderPhone": "11111111111",
+            "metadata": {
+                "userAgent": "userAgent",
+                "userIp": "255.255.255.255",
+                "language": "pt-BR",
+                "timezoneOffset": 3,
+                "extraData": "extraData"
+            },
+            "tags": [
+                "teste"
+            ]
+        }
+
+    return deepcopy(json_to_merchant_purchase(merchant_purchase_json))
+
+
+def generate_example_merchant_purchase_patch():
+    merchant_purchase_json = {
+        "status": "reversed",
+        "amount": 0
+    }
+    return deepcopy(merchant_purchase_json)
+
diff --git a/tests/utils/merchantSession.py b/tests/utils/merchantSession.py
new file mode 100644
index 00000000..1f9b3fe6
--- /dev/null
+++ b/tests/utils/merchantSession.py
@@ -0,0 +1,84 @@
+# coding: utf-8
+from copy import deepcopy
+from starkbank import MerchantSession
+
+
+def json_to_merchant_session(json_data):
+    return MerchantSession(
+        allowed_funding_types=json_data.get("allowedFundingTypes"),
+        allowed_installments=json_data.get("allowedInstallments"),
+        challenge_mode=json_data.get("challengeMode"),
+        expiration=json_data.get("expiration"),
+        tags=json_data.get("tags"),
+    )
+
+
+def generate_example_merchant_session_json(challengeMode):
+    merchant_session_json = {
+        "allowedFundingTypes": [
+            "debit",
+            "credit"
+        ],
+        "allowedInstallments": [
+            {
+                "totalAmount": 0,
+                "count": 1
+            },
+            {
+                "totalAmount": 120,
+                "count": 2
+            },
+            {
+                "totalAmount": 180,
+                "count": 12
+            }
+        ],
+        "expiration": 3600,
+        "challengeMode": challengeMode,
+        "tags": [
+            "yourTags"
+        ]
+    }
+    return deepcopy(json_to_merchant_session(merchant_session_json))
+
+
+def generate_example_merchant_session_purchase_challenge_mode_disabled_json():
+    merchant_session_purchase_json = {
+        "amount": 180,
+        "installmentCount": 12,
+        "cardExpiration": "2035-01",
+        "cardNumber": "5277696455399733",
+        "cardSecurityCode": "123",
+        "holderName": "Holder Name",
+        "fundingType": "credit",
+    }
+    return deepcopy(merchant_session_purchase_json)
+
+
+def generate_example_merchant_session_purchase_challenge_mode_enabled_json():
+    merchant_session_purchase_json = {
+        "amount": 180,
+        "installmentCount": 12,
+        "cardExpiration": "2035-01",
+        "cardNumber": "5277696455399733",
+        "cardSecurityCode": "123",
+        "holderName": "Holder Name",
+        "holderEmail": "holdeName@email.com",
+        "holderPhone": "11111111111",
+        "fundingType": "credit",
+        "billingCountryCode": "BRA",
+        "billingCity": "São Paulo",
+        "billingStateCode": "SP",
+        "billingStreetLine1": "Rua do Holder Name, 123",
+        "billingStreetLine2": "",
+        "billingZipCode": "11111-111",
+        "metadata": {
+            "userAgent": "Postman",
+            "userIp": "255.255.255.255",
+            "language": "pt-BR",
+            "timezoneOffset": 3,
+            "extraData": "extraData"
+        }
+    }
+    return deepcopy(merchant_session_purchase_json)
+