Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Changelog

All notable changes to this project will be documented in this file.

This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [3.2.0] - 2026-05-27

### Added

- **OAuth**: authorization URL builder, token exchange, and token refresh
(`sdk.oauth().get_authorization_url()`, `.create()`, `.refresh()`).
Enables marketplace and platform integrations to operate on behalf of
other sellers via the OAuth 2.0 authorization code flow.
Endpoints: `POST /oauth/token`.

- **Point**: in-person payment intent management for MercadoPago Point
devices (`sdk.point().get_devices()`, `.create()`, `.get()`, `.cancel()`).
Enables card-present transactions through physical Point card readers.
Endpoints: `GET /point/integration-api/devices`,
`POST /point/integration-api/devices/{device_id}/payment-intents`,
`GET /point/integration-api/payment-intents/{id}`,
`DELETE /point/integration-api/devices/{device_id}/payment-intents/{id}`.
Note: `change_operating_mode` (PATCH) is not included; requires HttpClient
PATCH support.

- **Invoice**: retrieval and search of subscription billing invoices
(`sdk.invoice().get()`, `.search()`). Enables monitoring of authorized
payments generated by PreApproval billing cycles.
Endpoints: `GET /authorized_payments/{id}`,
`GET /authorized_payments/search`.
137 changes: 137 additions & 0 deletions examples/order/create_order_automatic_payment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
"""Example: Automatic Payments flow with the MercadoPago Orders API.

Demonstrates both the first payment (storing the card credential) and a
subsequent recurring charge (referencing the previous transaction).
"""
import dataclasses
import mercadopago
from mercadopago.resources.order_automatic_payments import OrderAutomaticPayments
from mercadopago.resources.order_stored_credential import OrderStoredCredential
from mercadopago.resources.order_subscription_data import (
OrderInvoicePeriod,
OrderSubscriptionData,
OrderSubscriptionSequence,
)

sdk = mercadopago.SDK("YOUR_ACCESS_TOKEN")


# ── First payment ──────────────────────────────────────────────────────────
# Use first_payment=True and no prev_transaction_ref.
# The API stores the credential for future charges.

first_payment_order = {
"type": "online",
"total_amount": "100.00",
"external_reference": "subscription-001-payment-1",
"payer": {
"email": "customer@example.com",
"customer_id": "CUSTOMER_ID",
},
"transactions": {
"payments": [
{
"amount": "100.00",
"payment_method": {
"id": "master",
"type": "credit_card",
"token": "CARD_TOKEN",
"installments": 1,
},
"automatic_payments": dataclasses.asdict(
OrderAutomaticPayments(
payment_profile_id="PAYMENT_PROFILE_ID",
schedule_date="2026-07-01T00:00:00.000-04:00",
due_date="2026-07-05T00:00:00.000-04:00",
retries=3,
)
),
"stored_credential": dataclasses.asdict(
OrderStoredCredential(
payment_initiator="merchant",
reason="recurring",
store_payment_method=True,
first_payment=True,
# prev_transaction_ref not required on first payment
)
),
"subscription_data": dataclasses.asdict(
OrderSubscriptionData(
invoice_id="INVOICE_001",
billing_date="2026-06-01",
subscription_sequence=OrderSubscriptionSequence(number=1, total=12),
invoice_period=OrderInvoicePeriod(type="monthly", period=1),
)
),
}
]
},
}

result = sdk.order().create(first_payment_order)
first_order = result["response"]
print("First payment order:", first_order.get("id"), "→", first_order.get("status"))

# Save the transaction ID for use in the next charge.
first_transaction_id = (
first_order.get("transactions", {})
.get("payments", [{}])[0]
.get("id", "")
)


# ── Subsequent recurring charge ────────────────────────────────────────────
# Use first_payment=False and include prev_transaction_ref pointing to the
# previous charge. The API links this payment to the original authorization.

recurring_order = {
"type": "online",
"total_amount": "100.00",
"external_reference": "subscription-001-payment-2",
"payer": {
"email": "customer@example.com",
"customer_id": "CUSTOMER_ID",
},
"transactions": {
"payments": [
{
"amount": "100.00",
"payment_method": {
"id": "master",
"type": "credit_card",
"token": "CARD_TOKEN",
"installments": 1,
},
"automatic_payments": dataclasses.asdict(
OrderAutomaticPayments(
payment_profile_id="PAYMENT_PROFILE_ID",
schedule_date="2026-08-01T00:00:00.000-04:00",
due_date="2026-08-05T00:00:00.000-04:00",
retries=3,
)
),
"stored_credential": dataclasses.asdict(
OrderStoredCredential(
payment_initiator="merchant",
reason="recurring",
store_payment_method=False,
first_payment=False,
prev_transaction_ref=first_transaction_id, # required
)
),
"subscription_data": dataclasses.asdict(
OrderSubscriptionData(
invoice_id="INVOICE_002",
billing_date="2026-07-01",
subscription_sequence=OrderSubscriptionSequence(number=2, total=12),
invoice_period=OrderInvoicePeriod(type="monthly", period=1),
)
),
}
]
},
}

result = sdk.order().create(recurring_order)
recurring = result["response"]
print("Recurring charge order:", recurring.get("id"), "→", recurring.get("status"))
2 changes: 1 addition & 1 deletion mercadopago/config/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class Config:

def __init__(self):
"""Builds version-dependent values (user_agent, tracking_id)."""
self.__version = "3.1.1"
self.__version = "3.2.0"
self.__user_agent = "MercadoPago Python SDK v" + self.__version
self.__product_id = "bc32bpftrpp001u8nhlg"
self.__tracking_id = "platform:" + platform.python_version()
Expand Down
29 changes: 29 additions & 0 deletions mercadopago/examples/invoice/get_invoice.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from mercadopago import SDK


def main():
# Initialize the SDK with your access token
access_token = "<YOUR_ACCESS_TOKEN>"
sdk = SDK(access_token)

# Search invoices for a specific subscription (preapproval)
filters = {
"preapproval_id": "<YOUR_PREAPPROVAL_ID>",
"limit": 10,
}

try:
invoices = sdk.invoice().search(filters=filters)
print("Invoices found:", invoices["response"])

# Retrieve a specific invoice by ID
if invoices["response"].get("results"):
invoice_id = invoices["response"]["results"][0]["id"]
invoice = sdk.invoice().get(invoice_id)
print("Invoice details:", invoice["response"])
except Exception as e:
print("Error:", e)


if __name__ == "__main__":
main()
33 changes: 33 additions & 0 deletions mercadopago/examples/oauth/create_token.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from mercadopago import SDK


def main():
# Initialize the SDK with your marketplace access token
access_token = "<YOUR_ACCESS_TOKEN>"
sdk = SDK(access_token)

# Step 1: Build the authorization URL and redirect the seller to it
auth_url = sdk.oauth().get_authorization_url(
app_id="<YOUR_APP_ID>",
redirect_uri="https://yourapp.com/callback",
random_id="<UNIQUE_CSRF_STATE>",
)
print("Redirect seller to:", auth_url)

# Step 2: After the seller authorizes, exchange the received code for tokens
oauth_object = {
"client_secret": access_token,
"code": "<AUTHORIZATION_CODE_FROM_CALLBACK>",
"redirect_uri": "https://yourapp.com/callback",
"grant_type": "authorization_code",
}

try:
response = sdk.oauth().create(oauth_object)
print("Token created successfully:", response["response"])
except Exception as e:
print("Error:", e)


if __name__ == "__main__":
main()
37 changes: 37 additions & 0 deletions mercadopago/examples/point/create_payment_intent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from mercadopago import SDK


def main():
# Initialize the SDK with your access token
access_token = "<YOUR_ACCESS_TOKEN>"
sdk = SDK(access_token)

# List available Point devices to get a device_id
devices = sdk.point().get_devices()
print("Available devices:", devices["response"])

# Create a payment intent on a specific device
device_id = "<YOUR_DEVICE_ID>"
payment_intent_object = {
"amount": 1500,
"description": "Product purchase",
"payment": {
"installments": 1,
"type": "credit_card",
},
}

try:
response = sdk.point().create(device_id, payment_intent_object)
print("Payment intent created:", response["response"])

# Retrieve the payment intent status
payment_intent_id = response["response"]["id"]
status = sdk.point().get(payment_intent_id)
print("Payment intent status:", status["response"])
except Exception as e:
print("Error:", e)


if __name__ == "__main__":
main()
6 changes: 6 additions & 0 deletions mercadopago/resources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@
from mercadopago.resources.customer import Customer
from mercadopago.resources.disbursement_refund import DisbursementRefund
from mercadopago.resources.identification_type import IdentificationType
from mercadopago.resources.invoice import Invoice
from mercadopago.resources.merchant_order import MerchantOrder
from mercadopago.resources.oauth import OAuth
from mercadopago.resources.order import Order
from mercadopago.resources.payment import Payment
from mercadopago.resources.payment_methods import PaymentMethods
from mercadopago.resources.plan import Plan
from mercadopago.resources.point import Point
from mercadopago.resources.preapproval import PreApproval
from mercadopago.resources.preference import Preference
from mercadopago.resources.refund import Refund
Expand All @@ -34,11 +37,14 @@
'DisbursementRefund',
'HttpClient',
'IdentificationType',
'Invoice',
'MerchantOrder',
'OAuth',
'Order',
'Payment',
'PaymentMethods',
'Plan',
'Point',
'PreApproval',
'Preference',
'Refund',
Expand Down
62 changes: 62 additions & 0 deletions mercadopago/resources/invoice.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
"""Invoice resource for the MercadoPago Subscriptions API.

Wraps ``/authorized_payments`` endpoints to retrieve and search invoices
generated by subscription (pre-approval) billing cycles. Each invoice
represents a scheduled charge attempt against the subscriber's payment
method.

`API reference
<https://www.mercadopago.com/developers/en/reference/online-payments/subscriptions/get-authorized-payment/get>`_
"""
from mercadopago.core import MPBase


class Invoice(MPBase):
"""Provides read access to subscription invoices (authorized payments).

Each invoice corresponds to a billing cycle of a
:class:`~mercadopago.resources.preapproval.PreApproval` and tracks
the charge amount, status, retry attempts, and the resulting
payment. Use :meth:`get` and :meth:`search` to monitor billing
cycles and their outcomes.
"""

def get(self, invoice_id, request_options=None):
"""Retrieves a single invoice (authorized payment) by its ID.

Args:
invoice_id: Unique invoice identifier assigned by
MercadoPago.
request_options: Per-call configuration overrides.

Returns:
dict: Full invoice object including ``status``,
``transaction_amount``, ``preapproval_id``, and
associated ``payment`` details.

Reference: https://www.mercadopago.com/developers/en/reference/online-payments/subscriptions/get-authorized-payment/get
"""
return self._get(
uri="/authorized_payments/" + str(invoice_id),
request_options=request_options,
)

def search(self, filters=None, request_options=None):
"""Searches invoices matching the given filters.

Args:
filters: Query-string parameters such as ``preapproval_id``,
``status``, ``payer_id``, ``offset``, and ``limit``.
request_options: Per-call configuration overrides.

Returns:
dict: Paginated result with ``paging`` metadata and a
``results`` list of matching invoices.

Reference: https://www.mercadopago.com/developers/en/reference/online-payments/subscriptions/authorized-payment-search/get
"""
return self._get(
uri="/authorized_payments/search",
filters=filters,
request_options=request_options,
)
Loading
Loading